促膝笔谈
首发于促膝笔谈

对redis cluster的实现原理的理解,以及与codis,corvus的对比

Redis Cluster是一个分布式系统。由多个Redis实例组成的整体,数据按照Slot存储分布在多个Redis实例上,通过Gossip协议来进行节点之间通信。

整理理解

  • redis cluster整体上还是一个哈希分布的方案,与twemproxy,codis没有本质区别,不同的地方在于,客户请求不在通过代理节点转发,而是直接与对应槽位的节点处理
  • 通过gossip来使得集群中的节点互相了解其他节点的状况,(节点的存活情况,所持有的槽位情况等)

哈希方案:slot(槽位)

Redis Cluster中有一个16384长度的槽的概念,每个key都会通过公式CRC16(key)% 16384来计算键key属于哪个槽,槽位是虚拟的,可以在不同节点之前迁移

客户端对key做哈希,得到槽位,并到本地路由缓存查找槽位对应的服务器节点,其中会有一些特殊情况要处理

  • 访问到了错误的节点,该节点会返回正确的节点地址,让客户端重新访问
  • 访问到正在迁移的节点,分好几种情况
    • 访问的key还存储在旧的节点,直接操作
    • 访问的key已经迁移到新的节点了,但是整个槽位还没有完成迁移,返回一个一次性的ask 指令,让客户端尝试去新的节点查找这个key,但是不更新这个槽位在本地的路由缓存
    • 访问的key已经迁移,且整个槽位已经完成迁移,返回move指令,告诉客户端以后这个槽位都访问指定的新节点,客户端收到指令后会更新本地的路由缓存


集群通信

cluster服务端节点直接使用gossip协议进行节点间通信(redis cluster这种单纯的哈希分布的方案下,好像除了交换节点存活情况和槽位信息,服务端节点之间的数据交互需求并不高,感觉不如谷歌大数据老三篇论文里面的弱master节点的设计,能节省很多不必要的节点通信)

  • 主要使用 cluster meet ,ping ,pong 三个命令来完成
  • 通信由meet或ping命令发起
  • meet命令主要用于节点间的初次通信(?待确认)
  • 节点间的握手,类似于tcp的三次握手,都会确保对方知道自己已经收到消息
  • 定时任务clusterCron会向随机节点发其ping 通信(标记下线,疑似下线,即获知其他节点的存活情况)
  • 在定时心跳通信时,会附带上随机两个节点的信息,包括ip,端口,以及节点所包含的槽位信息
    • 收到心跳信息的节点,会判断附加的节点信息是否在本地记录中,
      • 本地无记录,会发其meet通信(握手)
      • 本地有记录,会进行更新(判断epoch)



  • 数据结构
    • 使用bitmap来表示一个节点持有的槽位信息
    • 集群消息处理函数 clusterProcessPacket


slot槽位的迁移

  • A节点的一个slot需要迁移到B节点,节点A 设置migrating flag, B 设置importing flag
  • 将该slot的key逐步迁移到节点B中
  • 迁移完毕使用cluster setslot node来消除importing和migrating flag
  • 这个时候节点B会bump epoch,更新专辑的epoch编号,来提高优先级,使得节点的槽位信息传播出去后能覆盖旧的版本
  • 节点槽位信息的更新:slot从A节点迁移到B节点后,信息同步到其他节点C,C节点会比较本地缓存的A节点的epoch与同步过来的B节点的epoch,B的epoch大,才对这个slot的信息进行更新

几种方案的对比

  • twemproxy 应该是最早开源的集群方案了,不过功能太过简单了,尽管使用了一致性哈希,但是集群中节点有增加时,还是会产生部分数据的丢失,而且不支持数据分片迁移,另外是单线程了,不过单线程的问题有唯品会twemproxies分支解决了
  • codis 的功能相对比较完整,支持新增节点,支持数据迁移,使用go语言开发,而go的协程非常适合这种并发请求的场景,也能轻松实现对多核CPU的利用,不过codis感觉最大的问题还是所有请求都需要结果代理转发,另外就是这个项目现在官方团队已经不再维护了(搞TiDB去了~)
  • redis cluster 这个redis官方的集群方案,优势和缺点也都很明显,与codis的预哈希方案类似,key哈希到某个slot(槽位)而不再是具体的节点,使得集群可以比较平滑的伸缩,另外一个优势就是客户端与node节点直连,省去了代理的开销,不过cluster的问题也同样明显,使用gossip这种无中心p2p的协议,导致所有节点都要频繁的与其他节点交换信息,另外一个问题就是使用redis cluster需要升级客户端,这对很多存量业务是很大的成本
  • corvus是饿了么开发的,加载redis cluster前面的一个客户端代理,主要作用是在不侵入代码的情况下使用redis cluster,业务代理里面对redis的使用与原来单点的实例没有区别
  • 另外还有SSDB/ledisdb/redisdb/tidis 等实现redis协议的第三方实现

总结

  • 在对redis的各种集群化方案比较思考之后,感觉加入一个弱的中心节点可能会是一个可以考虑的优化方向,redis cluster的p2p方式确实增加了通信成本,而且难以获知集群的当前状态,运维上也是一个问题,后续找时间需要研究下能否在codis方案下做一些调整(避免使用代理转发,保证主节点高可用,主节点失效时,通过选举产生新的主节点等)


参考:

Codis 的设计与实现 Part 1

Tidis - 基于tikv实现兼容redis协议分布式事务存储

黄光星:Redis Cluster 原理与管理

编辑于 2019-02-17

文章被以下专栏收录