V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
oncewosiwo
V2EX  ›  程序员

写了一篇对 redis cluster 实现方式以及其他其他方案的对比文章

  •  1
     
  •   oncewosiwo ·
    wosiwo · 2019-02-17 15:26:07 +08:00 · 2529 次点击
    这是一个创建于 2110 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 原理与管理

    3 条回复    2021-04-25 16:49:14 +08:00
    haiyangcn
        1
    haiyangcn  
       2019-02-18 10:06:18 +08:00
    请教一个问题:有什么方案可以实现:redis 分布式部署,并且可以多写(在任意节点写入后同步到其他节点)
    oncewosiwo
        2
    oncewosiwo  
    OP
       2019-02-18 10:19:22 +08:00
    @haiyangcn 好像目前并没有符合“任意节点写入”这个一条的方案,codis 是一个中心的代理服务器来接收所有请求,redis cluster 里的节点只接收其所管理的槽位下的 key 的请求。如果单纯说多节点写入的话,redis cluster 算是符合要求,客户端会自动将请求发送到对应的节点中,也会将请求同步到这个节点的从节点上
    MarkrMelon
        3
    MarkrMelon  
       2021-04-25 16:49:14 +08:00
    可以转载吗, 家里的网访问不了 v2
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3246 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 12:37 · PVG 20:37 · LAX 04:37 · JFK 07:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.