在使用redis-cluster之前你需要知道这些事
前段时间基础架构组、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节点,达到均衡负载的目的。
问题:连接建立失败的问题
/*
#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))问题:连接数过多
问题:CPU过载
问题:命令问题
鉴于我们遇到的大部分问题都和驱动内发送命令的逻辑相关,所以最后贴一张phpredis驱动向主节点发送命令逻辑的流程图:

a. 根据key所对应的slot找出负责该slot的Redis主节点
b. 若是与给定节点还没有建立链接,则先建立链接
c. 发送命令:不成功则转向下一个redis主节点,然后从b重新开始
d. 读取返回值:若正常返回,则结束;
e. 判断是否超时:超时了则抛出超时异常;
f. 判断是否发生moved错误:是则转向所moved错误所提示的redis节点,并从b重新开始;
g. 继续从b开始重试
说明:
上面判断的超时,循环之间是叠加的,不是每次循环开始又重新开始计时;
超时异常的产生主要有两个:一个是与负责给定slot的redis节点的连接一直建立失败,导致不断循环,然后超过时间限制;另一个,在给定时间限制内,没有从服务端读到返回值,直接超时,抛出异常。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!