Redis Cluster架构优化

Redis Cluster架构优化

在《全面剖析Redis Cluster原理和应用》中,我们已经详细剖析了现阶段Redis Cluster的缺点:

无中心化架构 客户端的挑战 Redis实现问题

当然之前也说过了:“这与Redis的设计初衷有关,毕竟作者都已经说了,最核心的设计目标就是性能、水平伸缩和可用性”。但综合来看,要想在生产环境中使用Redis Cluster,我们还是有一些工作要做的。本文就从宏观层面上,列举一些架构优化的参考方案。

1.P2P架构副作用1.1 Gossip通信开销

Gossip消息的通信开销是P2P分布式系统带来的第一个副作用。有一篇关于Gossip通俗易懂的文章《Life in a Redis Cluster: Meet and Gossip with your neighbors》。Redis为集群操作的消息通信单独开辟一个TCP通道,交换二进制消息:

PING/PONG:Cluster的心跳,每个结点每秒随机PING几个结点。结点的选择方法是:超过cluster-node-timeout一半的时间还未PING过或未收到PONG的结点,所以这个参数会极大影响集群内部的消息通信量。心跳包除了结点自己的数据外,还包含一些其他结点(集群规模的1/10)的数据,这也是“Gossip流言”的含义。MEET/PONG:只有MEET后的受信结点才能加入到上面的PING/PONG通信中。

关于Gossip的问题不可避免,我们只能通过参数调整和优化,在通信效率和开销之间找到一个平衡点。因为笔者还未搭建过大规模的Redis Cluster集群,关于集群的性能和参数调优还不能给出建议,留到积累足够经验再做整理吧。

1.2 不停机升级困难

以Nginx为例,修改配置甚至升级版本都不需要停机,Master会逐一启动新的Worker实例去替代旧的Worker。对于单机版的Redis,我们也可以用类似的方式实现的。但目前不知道Redis Cluster或者其他P2P分布式系统像Cassandra,是否有比较好的方案。

1.3 冷热数据无法区分

由于集群内结点都是对等的,所以像数据热度这种整体的统计数据就无处存放。当内存有限时,要想实现层次化存储,将冷数据Swap到慢存储如磁盘上时,就变得有些棘手了!

解决方法就是计算机界号称万能的“增加中间层”方法。增加一层Proxy,负责做数据统计、Swap甚至L1缓存。关于冷热数据的统计和处理,请参考《微博CacheService架构浅析》

2.客户端的挑战

Redis Cluster的引入会给客户端带来一些挑战。要么“勇敢面对”,通过引入最新的支持Cluster协议的Jedis客户端,再加以改造来应对这些挑战。要么增加Proxy,像防洪堤坝一样将危险隔离在外。

2.1 Cluster协议开发

对于Java,最主流的Jedis客户端已经早早开始支持Cluster协议了,但仔细看了一下,貌似处理集群中结点Failover时有些问题。Slave替换上来了,Jedis的确可以根据”MOVED”消息更新Slot与结点的对应关系,但是:

原来Master结点的连接池没有处理结点IP列表没有更新,极端情况下有问题

不知道这算不算Jedis由来已久的问题了。因为之前Jedis就是只支持要么用分片连接池,要么用Sentinel连接池,没有两者的结合!还好有热心的程序员“出手相助”,详见《Jedis分片Sentinel连接池实验》。上面两个问题对应的源码看得不是很细,突然想到的这两个问题,要是说的不对还请指正!

2.2 连接和路由表的维护

为了实现Smart客户端,Jedis要缓存16384个Slot到结点的映射关系。这还不算什么,Jedis还要为每个结点单独开一个连接池。假如你有一台强劲的32核服务器,为了在多核上充分释放Redis的处理能力,可能会起16甚至32个实例,想想会有多少连接建立?如果你有两台应用呢?

这个问题在像Hazelcast或GridGain等其他P2P系统中还不会这么严重。因为这两个产品都是用Java多线程开发的,每台服务器上起一个实例就可以了。这样客户端即便是Smart模式也不会开很多连接到服务器。后面还会讲到,单线程的Redis的运维成本也不小。

2.3 MultiOp和Pipeline支持有限

因为Redis Cluster自动数据Sharding的缘故,MultiOp和Pipeline都被限定在命令中的所有Key必须都在同一Slot内。如果想突破这个限定该怎么办?那扩展Jedis或者在Proxy中实现命令拆分和结果聚合的逻辑。

3.Redis实现问题

关于Redis的具体实现细节问题,主要是Redis简洁的设计、redis-trib等工具的欠缺导致的。我们可以通过Dashboard或Agent组件来解决这些问题。

3.1 不能自动发现

Redis Cluster没有使用传统的Multicast通知自动发现集群结点,我们能做的也只能是像redis-trib那样,在用户指定新结点时帮它执行CLUSTER MEET命令。

3.2 手动Resharding

手动指的不只是像Codis那样要在控制台上添加完新结点后手动触发Rebalance,而是要我们指定哪些Slot迁移到哪些结点上!就像建立集群时做的那样!如果我们有个统一的Dashboard,实现个简单的根据各个机器和Slot负载进行Resharding的算法,那么就能将这部分工作自动化了。

3.3 无监控管理UI

Redis一直没有官方的监控管理工具,到了Redis Cluster依旧是这个样子。这个问题比较好解决,像Codis那样提供一套漂亮的Dashboard就可以了,底层使用各种CLUSTER命令完成工作。

3.4 “脑裂”问题

关于“脑裂”(网络分区)问题,只能靠Redis官方提供解决方案了。

3.5 迁移速度较慢

GitHub上有人提了一个Issue “redis-trib: use pipeline to speed up moving slot”,通过Pipeline调用Migrate命令,改善redis-trib的迁移速度。但这样只是治标不治本,毕竟迁移的基本单位还是Key而不是Slot。但因为Redis的save/bgsave都是实例级别,所以要想不改Redis源码就获得Slot的复制或迁移能力,还真不太好办!

看看Codis作者的思路:“在RebornDB中我们会尝试提供基于复制的迁移方式,也就是开始迁移时,记录某slot的操作,然后在后台开始同步到slave,当slave同步完后,开始将记录的操作回放,回放差不多后,将master的写入停止,追平后修改路由表,将需要迁移的slot切换成新的master,主从(半)同步复制,这个之前提到过。”

3.6 迁移故障恢复

由于无中心化的设计,数据迁移的进度等信息无处保存。如果迁移中发生失败,则可能某一个Slot处于迁移中间状态。再加上没有进度信息的话,会给我们的恢复工作带来很大麻烦。可以考虑重新启用ZooKeeper,或者单独使用一个Redis实例做全局信息存储。

3.7 Slave冷备

对于Redis Cluster不会将请求转发给Slave结点,造成Slave冷备的问题,可以靠Proxy做读写分离来解决,当然这样会牺牲一部分的一致性。

4.优化方案总结4.1 架构变迁

在解决上面各种问题时我们引入了三个组件:Proxy、Dashboard和Agent。这三个组件都担当一定的职责,但这三个组件不一定非要对应部署三个子系统。根据需要,可以选择去掉或合并来简化设计。

4.1.1 Proxy组件志在山顶的人,不会贪念山腰的风景。

Redis Cluster架构优化

相关文章:

你感兴趣的文章:

标签云: