关闭

在使用redis-cluster之前你需要知道这些事

时间: 2018-11-10阅读: 3401标签: redis

前段时间基础架构组、DBA还有云盘团队一起推广了phpredis的RedisCluster的线上使用,目前线上业务已经稳定,单业务的规模水平是:Qps平均15W,数据量在700G左右。现对这段时间的工作和所遇到的一些常见问题进行简单总结:


问题:fd泄漏

现象:最开始试水phpredis的Cluster功能时,总是遇到’Couldn’t map cluster keyspace using any provided seed’的错误

原因:这是由于早期phpredis驱动的fd泄漏问题造成的。每新建一个RedisCluster客户端,客户端会向给定的Seeds节点获取Redis集群的Slots分布信息,但是为了获取slots信息而向Seeds节点建立的连接是不用于后期发送命令使用的,所以在获取slots信息结束后应该释放掉改连接,而phpredis没有释放,这样在新建若干次RedisCluster客户端后,就fd泄漏了,导致连接建立失败,slots信息也就无法获取了,从而报错。

解决办法:更新phpredis的驱动,后期的phpredis驱动已经修复这个问题 (是我们提交的, wakaka……!)


问题:TimeWait过多导致“Timed out…“错误

现象:客户机上timewait端口数量比较多时,‘Timed out…’报错量比较

分析:我们观察到,当timewait端口数量较多时,会存在两个线程进程绑定绑定到同一个端口,但是却只有一个进程的连接建立成功了,另一个进程的连接建立要在已建立连接断开3s(SYN重试时间)后才能成功,而3s的时间延迟,而3s的延迟在业务方看来是不能容忍的,一般肯定会报“Time out…”的错误。而且随着timewait的数量的增加,这种情况发生的频率也递增;

解决办法:使用长连接,或者限制timewait端口数


问题:获取Slots信息比较耗性能

原因:因为每新建一个RedisCluster客户端都会向给定的Seeds节点拉去Redis集群的Slots信息,因为要建立连接和命令的特殊性,这个过程是相当耗时的(局域网内测试:2ms左右),当在新建RedisCluster的比较频繁的场景下,是比较的耗性能的

解决办法:修改phpredis驱动,在本地缓存住Redis集群的的Slots信息,把它放在持久性资源内。这样每次新建一个RedisCluster客户端时就不用再走网络获取Slots信息,直接在本地缓存中就可以拿到这些信息了。

备注:

a. 在我们的推动下,作者也出了一个这样的功能,在cache-slots分支上;
b. 驱动在拉去Slots信息时,总是按顺序选择依次选择第一个,第二个……这样第一个Seed节点的压力就会比较大,所以如果在新建RedisCluster客户端比较频繁的场景下,应该使用Qconf这样的工具或者修改驱动,以随机选择Seed节点,达到均衡负载的目的。


问题:连接建立失败的问题

现象:某一个客户端进程持续大量报“Timed out attempting…”的问题
原因: 这个也是phpredis驱动的bug,产生这个问题有两个原因,一是驱动懒加载问题,当一个连接还未建立时,phpredis的做法是先把连接标志给置位,然后再建立链接,这样当链接建立失败了,下一次的判断却认为是成功的,也就没有机会重新建立连接了;二是连接存活检测的一个bug,当一个连接被认为是死”了, phpredis的做法是连续重连十次,如果都不能建立成功就退出,但是退出前所改变的状态,并不能让该连接有资格尝试下一次重连,这样的话当服务端因为某些原因再这十次重连的时间段内没有恢复过来(卡机了或着挂了等),等他恢复过来后,这个链接按理说是应该且可以恢复的,但是却因为没有机会重连而永远失效。
解决办法:在每次的存活检测后,如果失效都把连接的状态置为有资格重连的状态
/*
 #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \  
    (sock && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \  
      php_stream_write(sock->stream, buf, len)==len)  
*/  
#define CLUSTER_SEND_PAYLOAD(sock, buf, len) \  
     ((sock && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \  
     php_stream_write(sock->stream, buf, len)==len) || \  
     ((sock->stream == NULL ? : redis_stream_close(sock)), \  
      sock->lazy_connect = 1, \  
        sock->status = REDIS_SOCK_STATUS_DISCONNECTED, \  
       0))


问题:连接数过多

现象:客户机增加时,redis服务端的连接数激增,影响服务的稳定性
原因:线上大部分客户机一般使用fpm模式来运行客户端的,进程数是128。官方的phpredis版本(cache-slots分支)是必须同时缓存集群的Slots信息和客户端到服务端的连接,这样为了使用缓存集群Slots信息的功能,服务端的连接数也被缓存了,造成连接数激增。
解决办法:修改驱动,使得缓存集群slots信息和缓存连接相互独立
备注:理论上来说,使用长连接(缓存连接)的好处要大于短连接,它避免了连接建立上的开销和TW过多等问题,在连接数不大的情况下,应该尽量用长连接来访问


问题:CPU过载

现象:访问量增加后,客户端报“invaild reply for Del…”,并伴随着“Timed out…”错误
原因:经过一段时间的排查,这是由于CPU压力过载导致的。CPU的压力太大,客户端发送过来的命令在给定的时间内(100ms)得不到返回值,也就报“Timed out…“的错误,之所以单单Del会报“invalid reply…”的错误,这是因为Del命令的返回值检查比较的严格,它的返回值必须是整数型的,而其他的命令返回值检查相对宽松。
解决办法:针对这一问题,只能扩容了。


问题:命令问题

现象:phpredis的RedisCluster客户端是不支持密码验证的,这就给数据库的安全带来了一定的风险
解决办法:修改phpredis驱动,加上密码验证的功能
备注:phpredis的Pull Requests里,有人已经提交了这个功能,如果有这方面强需求的可以使用或者参照着自己实现(链接)

鉴于我们遇到的大部分问题都和驱动内发送命令的逻辑相关,所以最后贴一张phpredis驱动向主节点发送命令逻辑的流程图:


a. 根据key所对应的slot找出负责该slot的Redis主节点
b. 若是与给定节点还没有建立链接,则先建立链接
c. 发送命令:不成功则转向下一个redis主节点,然后从b重新开始
d. 读取返回值:若正常返回,则结束;
e. 判断是否超时:超时了则抛出超时异常;
f. 判断是否发生moved错误:是则转向所moved错误所提示的redis节点,并从b重新开始;
g. 继续从b开始重试

说明:

上面判断的超时,循环之间是叠加的,不是每次循环开始又重新开始计时;

超时异常的产生主要有两个:一个是与负责给定slot的redis节点的连接一直建立失败,导致不断循环,然后超过时间限制;另一个,在给定时间限制内,没有从服务端读到返回值,直接超时,抛出异常。

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/1319

关闭

Redis常用命令

连接操作命令,持久化,远程服务控制,对value操作的命令,String,List,Set,Hash,Redis 发布订阅命令,Redis 事务命令,查看keys个数,清空数据库

Redis可视化管理工具有哪些?

redis 是当前非常流行的缓存数据库,得益于其简单的 key-value 模式的数据存储和丰富的数据类型与事件机制使得 redis 成为当前后端开发中不可或缺的利器。下面推荐一些好用的 redis 的管理工具

Redis为什么变慢了?

Redis作为内存数据库,拥有非常高的性能,单个实例的QPS能够达到10W左右。但我们在使用Redis时,经常时不时会出现访问延迟很大的情况,如果你不知道Redis的内部实现原理,在排查问题时就会一头雾水。

Redis的主从复制

Redis配置成主从模式,主库(Master)只负责写数据,从库(Slave)只负责读数据。一个主库可以拥有多个从库,但一个从库只能隶属于一个主库。

PHP操作Redis常用技巧总结

Redis连接与认证连接参数:ip、端口、连接超时时间,连接成功返回true,否则返回false,设置键值:成功返回true,否则返回false,获取键值:成功返回String类型键值

Redis的正确使用姿势

说到分布式缓存,可能大多数人脑海浮现的就是redis了,为什么redis能够在竞争激烈的缓存大战中脱颖而出呢?原因无非有一下几点:性能好,丰富的特性跟数据结构,api操作简单。但是用的人多了,就会出现很多不规范或者疏忽的地方,严重的时候甚至会导致生产事故

node怎么用redis怎么做消息队列?

MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。

Redis 的各项功能解决了哪些问题?

官方简介解释到:Redis是一个基于BSD开源的项目,是一个把结构化的数据放在内存中的一个存储系统,你可以把它作为数据库,缓存和消息中间件来使用。同时支持strings,lists,hashes,sets,sorted sets

Redis的基本数据结构及基本命令详解

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)

使用 Redis 构建先进先出队列解决秒杀问题

先进先出队列(first in first out queue)是一种非常常见的数据结构, 一般都会包含入队(enqueue)和出队(dequeue)这两个操作, 其中入队操作会将一个元素放入到队列中, 而出队操作则会从队列中移除最先被入队的元素

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!