当前位置:首页 > 问答 > 正文

哨兵Redis用着还行但也有不少坑和不太方便的地方需要注意

哨兵Redis用着还行,但也有不少坑和不太方便的地方需要注意,我结合自己用的经验和一些公开的技术分享,Redis开发与运维》这本书里提到的一些点,还有社区里很多人的吐槽,来说说具体的情况。

第一个大坑是主从切换的时候,可能会丢数据,这是最让人头疼的,哨兵的目标是高可用,它发现主节点挂了,就会选一个从节点扶正成新的主节点,这个切换过程不是天衣无缝的,根据《Redis开发与运维》里的分析,在旧主节点宕机前,它可能有一部分数据还没来得及同步给从节点,这部分数据就永远丢失了,虽然Redis有异步复制和WAIT命令之类的机制,但在哨兵自动切换的紧急情况下,很难保证强一致性,也就是说,你的应用可能会看到数据“回滚”了,几分钟前写进去的数据,切换后再查就没了,这对业务来说有时是致命的,比如订单状态、支付信息。

第二个不方便的地方是配置管理挺繁琐的,哨兵模式里,你不是直接连Redis服务器,而是连哨兵节点来获取当前真正的主节点地址,这意味着,你的每个客户端应用里,配置的不是一个固定的Redis地址,而是一串哨兵节点的地址列表,运维的时候,加一个哨兵节点或者改个端口,你得同时改客户端的配置和所有哨兵实例自己的配置文件,哨兵之间的配置并不会自动同步,你得一个个去改,或者用脚本批量操作,很麻烦,如果配置没对齐,哨兵集群自己可能就会出问题,比如选举时票数算不对。

哨兵Redis用着还行但也有不少坑和不太方便的地方需要注意

第三个问题是客户端支持参差不齐,理论上,客户端库应该支持哨兵模式,自动发现主节点,但实际用起来,各家客户端库的实现水平和“脾气”不一样,有些老旧的或者不太流行的客户端库,对哨兵协议支持得不好,比如连接池在故障切换后不会自动更新连接,导致应用持续报错,必须重启,就算是用成熟的客户端,比如Java的Jedis或Lettuce,你也得仔细研究配置项,像“连接超时时间”、“哨兵查询重试策略”这些,调不好一样出问题,很多开发者在博客里抱怨,明明哨兵已经切换成功了,但应用还是卡住很久,就是因为客户端这边的配置没跟上。

第四个是监控和运维上的盲点,哨兵自己会监控Redis实例,但谁来监控哨兵呢?你需要额外的监控系统来盯着哨兵进程是否存活,更纠结的是,哨兵判断主节点下线依赖于一套投票机制,如果网络出现轻微波动(比如瞬间丢包),但还没到完全不通的地步,可能会导致其中一部分哨兵认为主节点“主观下线”,从而引发不必要的故障切换投票,甚至可能引发“脑裂”的混乱情况(虽然最终算法会避免,但过程惊心动魄),监控的时候,你不仅要看Redis的指标,还得时刻关注哨兵集群的“主观下线”和“客观下线”事件日志,心很累。

哨兵Redis用着还行但也有不少坑和不太方便的地方需要注意

第五个是资源消耗和性能考虑,很多人觉得哨兵就是个轻量级的进程,没关系,但实际上,在生产环境,为了可靠性,哨兵节点至少要部署三个,而且最好分散在不同的物理机或虚拟机上,这就多了三台机器的资源开销,更重要的是,哨兵节点本身也需要网络通信和定期的心跳检测,网络不好的时候,它们之间的通信延迟会直接影响故障判断的速度,有的运维人员分享过,在虚拟化环境或云环境下,网络偶尔抖一下,哨兵集群就可能变得“神经紧张”,频繁发送警告,增加了运维的恐慌。

还有一个隐形的坑:它对读写分离的支持比较“原始”,哨兵只负责告诉客户端谁是主节点,它不会自动把读请求分发到从节点上去,实现读写分离完全要靠客户端自己,客户端需要从哨兵那里拿到所有从节点的列表,然后自己决定把读命令发到哪个从节点,这增加了客户端的复杂性,而且从节点的数据有延迟,客户端得处理可能读到旧数据的情况,如果你想扩从节点,加了一个新的,客户端还得重新从哨兵获取配置才能感知到,不是完全无缝的。

哨兵Redis解决了最基本的自动故障切换问题,让你晚上能睡得着一点,但用起来之后,你会发现需要操心的地方一点没少,从数据一致性、客户端兼容性到日常监控,都有不少细节要打磨,算不上一个“省心”的方案,很多团队最后遇到规模大了或者业务要求高了,就会考虑迁移到更复杂的Redis Cluster或者其他架构上去。