性能文章>Redis Cluster去中心化设计的思考>

Redis Cluster去中心化设计的思考原创

1年前
435666

大家好,我是石页兄

一、背景

分布式存储⼀般都是通过多副本的形式保证数据可靠,多副本之间使⽤⼀致性协议保证数据⼀致,所以⼀般都需要⼀个 Leader 节点负责协调写⼊,常见的分布式存储如Zookeeper的工作模式即是 Leader 协调写入,⾼并发场景下 Leader 单节点会成为系统的瓶颈,单节点的瓶颈本质就是中心化的问题。解决的办法可考虑采用去中心化的设计,比如 Redis 的 Cluster 模式。

二、宏观视角看 Redis Cluster 去中心化

Redis Cluster 模式 采用了去中心化的设计并且具备以下能力:

1) 可扩展性

  • 横向扩展,通过增加机器实现增加能力上限
  • 读写扩展,基于主从模式,通过读写角色分离,增强读写能力。

2) 高可用

  • 避免单机故障
  • 主从模式

3)负载均衡

  • 数据分治
  • 数据迁移
  • 请求智能路由

4) 错误恢复

  • 自动主从切换

三、微观视角看 Redis Cluster 去中心化

3.1 数据分治问题

本质就是数据和请求落在哪个节点上,或者说是一个节点都负责哪些数据的问题,即分布式存储,需要通过 数据分区策略(算法)来解决,并且需考虑以下问题:

  • 可用性:仅部分不可用
  • 维护方便 :仅针对不可用部分,针对性修复
  • 均衡 IO,数据和请求均衡到不同的节点中
  • 改善查询性能,数据精准定位到节点中

在以下分区算法中,个人比较偏好 Redis cluster 的虚拟槽分区

1) 范围分区

同一范围内的数据聚集连续,节点跨度小

2) 节点取余分区

实现简单,扩缩容时需迁移的数据量大,整倍扩容时,相对迁移的数据量少

3)一致性 hash

  • 将数据散落在 2 的 32 次方个 token(槽点更容易理解),节点随机分配到不同的 token,即每个节点管理一定连续范围的 token,数据 key 通过 hash 得到 token 后,即可知自己所属的节点

  • 它能够在添加/移除一台缓存服务器的时候,尽可能小的改变已存在 key 的与节点映射关系,避免大量 key 的重新映射

  • 服务节点太少时,容易因为节点分布不均匀而造成数据倾斜问题,然后引入虚拟节点的方式来解决数据散落的平衡性问题,增加的这一层跟下边的虚拟槽有异曲同工之处

即使如此,一致性 hash 对数据分布和节点位置的控制依然不够友好

《一切皆是映射:代码的本质》一致性哈希算法(consistent hashing)[1]一致性 hash 的一些问题[2]

4)带有限负载的一致性哈希

因为一致性哈希算法的数据分布不均匀的问题,Google 在 2017 年提出了带有限负载的一致性哈希算法来解决这个问题,带有限负载的一致性哈希算法思想比较简单,给每个存储节点设置了一个存储上限值来控制存储节点添加或移除造成的数据不均匀,当数据按照一致性哈希算法找到相应的存储节点时,要先判断该存储节点是否达到了存储上限;如果已经达到了上限,则需要继续寻找该存储节点顺时针方向之后的节点进行存储

5)虚拟槽分区

  • 通过引入一层(槽),解耦数据和节点的关系 key 和槽的映射不变,槽在哪个节点的关系可变,槽成为集群内数据管理和迁移的基本单位,而槽的粒度小、边界清晰,简化了节点扩容和收缩难度,只需要关注数据在哪个槽,并不需要关心数据在哪个节点上。

  • 固定映射关系,放大分布空间,数据均匀分布 虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。这个范围一般远远大于节点数,Redis Cluster 槽范围是 0~16383。采用大范围槽的主要目的是为了方便数据拆分和集群扩展。每个节点会负责一定数量的槽

虚拟槽分区可以说比较好的兼容了数据均匀分布和扩展性的问题。

3.2 链接管理问题

通过 redis 虚拟槽分区的实现,基于分治原则每个节点都具备了独立的读写能力,并且管理一部分数据, 那么客户端可以与每个节点独立建立链接,并做池化处理;这就从根本上解决了需要 leader 单点受限的问题。

3.3 寻址路由问题

  • key-slot 的关系是固定的
  • slot 和 节点的关系是可变的(分配策略决定),并且由节点管理 那么客户端在得到 slot 的时候也是不知道这个 slot 是在哪个节点上的;只有请求到节点后,如果是在当前节点上则直接给出值,如果不在当前节点上,会返回当前 slot 所在的节点,需要一个重定向的操作。

重定向是高频操作,会带来性能损耗,而且在 slot 跟节点的关系不变更的情况下这个重定向也是重复的。数据 slot 的变更是低频的,如果这个 slot 和节点的映射关系,能够在向节点发起请求前明确,那么就会避免重定向的问题。那么通过同步 slot 和节点的映射关系(拓扑信息),即可实现智能寻址路由。

3.4 数据迁移问题

难免会遇到扩缩容的场景,优雅轻便的在线的方式将是我们的诉求。参考 redis cluster 的方式

  1. 启动新节点
  2. 检查是否孤立节点,并加入集群
  3. 配置数据同步(新从加入)、数据迁移的计划
  4. 小批量多次的将 solt 中的数据完成迁移,并通知 slot 和节点映射的变更

缩容的情况下会有额外的节点下线逻辑。

特别注意:

数据迁移未完成前,并没有通知 slot 和 节点映射的变更,那么智能客户端依然会去原节点访问数据,此时需要另外一种重定向机制告诉客户端,数据正在迁移,新节点在哪里,你去新节点看看,这种机制就优雅的解决了在线迁移并保证了数据一致性;但需清楚这是有一定的性能损耗的,能否在系统高负载的时候做,需要压测演练。

我是石页兄,如果这篇文章对您有帮助或启发的话,欢迎关注笔者微信公众号【架构染色】进行交流和学习

参考资料

①《一切皆是映射:代码的本质》一致性哈希算法(consistent hashing): https://www.jianshu.com/p/8a608bc87680②一致性 hash 的一些问题: https://www.jianshu.com/p/15697cafe487③为什么 Redis 集群有 16384 个槽  : https://www.cnblogs.com/rjzheng/p/11430592.html

 

点赞收藏
石页兄
请先登录,查看6条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

Redis系列:RDB内存快照提供持久化能力

Redis系列:RDB内存快照提供持久化能力

Redis stream 用做消息队列完美吗?

Redis stream 用做消息队列完美吗?

API性能调优
6
6