你站在桥上看风景,看风景的人在楼上看你。
明月装饰了你的窗子,你装饰了别人的梦。
这个7月注定不平凡,通过7月连续的redis故障,细心如你,一定会对技术,公司,同事,职业有了更深刻的认识和反思。先回忆下吧
我司redis部署特点
1. 集中部署,N台机器专职负责某个产品线。
2. 传统twemproxy方式,额外会有自已定制几套twemproxy。
可以看出来,非常传统的方式。开始只有一个default集群,php所有功能获取redis句柄都是这个,流量增长后开始按功能划分。5月中旬我来到公司,开始推进 redis cluster,争取替换掉twemproxy,制定了如下方案:
redis cluster -> smart proxy -> php
集群模式能够做到自动扩容,可以把机器当成资源池使用。在php前面部署基于cluster的smart proxy,这是非常必要的,后文会说到。最后方案被PK掉了,改成了 php redis扩展直连 redis cluster。方案如下:
redis cluster -> php redis 扩展
1. redis master -> redis-port -> smart proxy -> redis cluster
2. 修改php config, gitlab 发布上线,使用新集群配置
3. 停掉老twemproxy集群,完成迁移
方案看似简单,实际使用要慎重。大家都知道redis rdb bgsave会使线上卡顿,低峰期做,并且轮流 redis master同步,千万不能同时用redis port 做sync。
故障总是不期而遇-网卡
想起《东京爱情故事》主题曲,突如其来的爱情,不知该从何说起。
zabbix监控图故障的图找不到了,截图一张正常网卡流量图,千兆网卡在某个周五23:00业务高峰期被打满,导致线上请求失败。如坐针毡的波峰图。如前文所说,我司集中部署redis,此业务是线上cache个人详情页登陆相关的,一共4台机器,每台20实例,无法做到立刻扩容,紧急之下RD同学降级,抛掉前端30%的请求,恢复后高峰期已过。
Leader要求周六所有人加班去迁移,2点多大家睡了,恩,就这样睡了。故障暂时解决,故事依然继续。周六上午10点,市场运营推送消息,导致人为打造了小高峰,如坐针毡的波峰,服务立马报警,紧急之下立马再次抛掉30%请求。然后紧急搭建两套不同功能的redis cluster集群,采用冷启动的方式,一点点将cache流量打到新集群中,mysql 几台从库 qps 一度冲度8K。
针对网卡最后引出两个解决方案:
1. 所有redis 机器做双网卡bonding,变成2000Mbps。
2. 所有redis产品线散开,混合部署打散。
3. 增加网卡流量监控,到达60%报警。
反思:
为什么要睡觉?而不是连夜迁移?做为运维人员,危险意识不够足
还有一起网卡故障,是应用层bug,频繁请求大 json key 打满网卡。当时QPS稳定保持在20W左右,千兆网卡被打满。临时解决方案直接干掉这个key,过后再由RD排查。
思考:
监控报警不到位,对于创业公司比较常见,发生一起解决一起。针对这类问题,有两个想法:qps报警,比如阀值定在2W。还有一个在proxy上做文章,对key的访问做限速或增加key的屏蔽功能。QPS报警后运维人员排查,可能已经产生影响了,在proxy层做对性能会有影响。
你这该死的连接数
某天8点40左右,还在地铁的我接到电话,redis连接报错,貌似几个实例的连接数被打满。这个故障持续时间较长,php redis 扩展直连redis cluster,连接持续增长,直到打满完全连不上。后来经过排查,确认是扩展 bug,导致老连接不释放。同时原因也很多:
1. 我司使用redhat7,所有的应用都是由systemd管理,启动没有指定LimitNOFILE,导致redis maxclients限制死在4000左右。
2. php redis 扩展 bug,连接不释放,线下稳定复现。
这几次连续故障很严重,leader直接决定全部回退到老的twemproxy版本,最后回退了两个最重要的产品线。
反思:
1. 架构改动没有经过充分测试,线下稳定复现的bug没有仔细测试直接上线。
2. 运维意识不足,对systemd了解不够深入,没有对所有配置做严格检查。
3. 做为世界上最好的语言,偶尔还是有些问题,最好在redis和php间隔层proxy,将后端redis保护在安全的位置。
Cluster脑裂是什么鬼?
脑裂在所谓的分布式系统中很常见,大家也不陌生,做为DBA最怕的就是mysql keepalived脑裂,造成主库双写。难道 redis cluster中也会有脑裂么?
凌晨5点接到电话,应用看到数据不一致, 偶尔是无数据,偶尔有数据,很像读到了脏数据。mysql 在多个从库上做读负载均衡很常见,redis cluster也会么?登上redis , cluster nodes, cluster config,确实发现不同redis实例配置了不同的cluster nodes。想起了昨天有对该集群迁移,下掉了几个实例,但是在php配置端没有推送配置,导致php可能读到了旧实例数据,马上重新推送一遍配置,问题解决。
反思:
1. 有任务配置的变更,一定考虑好所有环境的连动。这也是当前配置无自动发现的弊端。
2. 屏蔽细节,在redis cluster上层做proxy的重要性再一次验证。
3. 运维意识不足,严重的人为故障。
Bgsave传统的典型问题
问题很典型了,非常严重的故障导致redis OOM。
解决方案:
单台机器不同端口轮流bgsave,内存不足时先释放cache,释放失败拒绝再bgsave并报警
主库重启flush掉从库
考虑不周,备份时,只在slave上bgsave。主库由于某些原因重启,立马被systemd拉起,时间远短于cluster选举时间。后面就是普通redis master/slave之间的故事了,master加载空dump.rdb,replicate到slave,刷掉slave数据。
解决方案:
1. 备份的同时,将dump.rdb rsync到主库datadir目录下面一份。
2. 根据redis用途,做存储使用的redis systemd去掉auto restart 配置。
其它典型故障/问题
1. 应用设计问题,部分hset 过大,一度超过48W条记录,redis频繁卡顿感。
2. 使用redis做计数器,占用过大内存空间。这个redis官网有解决方案,利用hash/list的线性存储,很有效。但是由于mget无法改造,我们没采用。
3. 混布,导致部份产品线消耗资源过高,影响其它所有实例。
4. 机房IDC故障,单个机柜不通,里面所有混布的产品线无法提供请求,数据请求失败。
5. 应用端分不清cache/storage,经常可以做成cache的key,不加ttl导致无效内存占用。
写在最后?
虽然写在最后,但远没有结束,征程才刚刚开始。
每次故障都是一次反思,但我们拒绝活在过去,生活还要继续。
我司重度依赖redis,除了图片其它所有数据都在redis中。在稳定为主的前提下,还在向redis cluster迁移,其中有几个问题还待解决:
1. redis 实例级别高可用,机柜机别高可用。
3. 隔离上层语言与redis,提供稳定的smart proxy接口。
4. redis 集群build和交付,缺少配置集中管理。
5. 很多集群qps并不高,内存浪费严重,急需持久化redis协议存储,基于ardb/ledisdb的sharding是个方案,自已开发需要同事的信任,这点很重要。
6. 最终我司线上存在两个版本,twemproxy开启auto_reject_host做cache集群,redis cluster + smart proxy做存储。