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

更新日期: 2018-11-10阅读: 4.9k标签: 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节点的连接一直建立失败,导致不断循环,然后超过时间限制;另一个,在给定时间限制内,没有从服务端读到返回值,直接超时,抛出异常。

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

Redis 5种主要数据类型和命令

redis常用数据结构strig、list、hash、set、zset,这是最常用的5中redis数据结构,其实还有些不太常用的数据结构比如:HyperLogLog、GeoHash、PubSub等

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

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

Redis 中 Lua 脚本的应用和实践

前段时间组内有个投票的产品,上线前考虑欠缺,导致被刷票严重。后来,通过研究,发现可以通过 redis lua 脚本实现限流,这里将 redis lua 脚本相关的知识分享出来,讲的不到位的地方还望斧正。

Redis在Php项目中的实际应用场景

Redis在Php项目中的实际应用场景:商品维度计数、用户维度计数、存储社交关系、用作缓存代替memcached、反spam系统、用户Timeline/Feeds、最新列表&排行榜、消息通知、将Redis用作消息队列

PHP使用redis防止大并发下二次写入

php调用redis进去读写操作,大并发下会出现:读取key1,没有内容则写入内容,但是大并发下会出现同时多个php进程写入的情况,这个时候需要加一个锁,即获取锁的php进程有权限写。

Redis的主从复制

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

Redis常用命令

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

分布式锁的redis缓存使用方式

目前有很多成熟的缓存产品,包括Redis,memcached等。这里以Redis为例来分析下使用缓存实现分布式锁的方案。主要的实现方式是使用Jedis.setNX方法来实现。以上实现方式同样存在几个问题:

Redis的正确使用姿势

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

redis用在哪里?redis 应用场景

Redis是一个由Salvatore Sanfilippo写的key-value存储系统。Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

点击更多...

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