文章目录

  • 1 :peach:基本概念:peach:
  • 2 :peach:数据分片算法:peach:
  • 2.1 :apple:哈希求余:apple:
  • 2.2 :apple:⼀致性哈希算法:apple:
  • 2.3 :apple:哈希槽分区算法:apple:
  • 3 :peach:面试高频出现问题:peach:
  • 3.1 :apple:Redis 集群是最多有 16384 个分片吗?:apple:
  • 3.2 :apple:为什么是 16384 个槽位?:apple:



1 🍑基本概念🍑

我们知道哨兵模式提⾼了系统的可⽤性,但是真正⽤来存储数据的还是 master 和 slave 节点。所有的数据都需要存储在单个 master 和 slave 节点中,如果数据量很⼤, 接近超出了 master / slave 所在机器的物理内存, 就可能出现严重问题了。

那么如何获取更⼤的空间? 加机器即可! 所谓 “⼤数据” 的核⼼, 其实就是⼀台机器搞不定了, ⽤多台机器来搞。
Redis 的集群就是在上述的思路之下, 引⼊多组 Master / Slave , 每⼀组 Master / Slave 存储数据全集的⼀部分, 从⽽构成⼀个更⼤的整体, 称为 Redis 集群 (Cluster)。

假定整个数据全集是 1 TB, 引⼊三组 Master / Slave 来存储,那么每⼀组机器只需要存储整个数据全集的 1/3 即可。

redis 双机房集群 redis集群几台机器合适_redis

在上述图中:

  • Master1 和 Slave11 和 Slave12 保存的是同样的数据. 占总数据的 1/3
  • Master2 和 Slave21 和 Slave22 保存的是同样的数据. 占总数据的 1/3
  • Master3 和 Slave31 和 Slave32 保存的是同样的数据. 占总数据的 1/3

这三组机器存储的数据都是不同的,每个 Slave 都是对应 Master 的备份(当 Master 挂了, 对应的 Slave 会补位成Master)。每个红框部分都可以称为是⼀个分片(Sharding)。如果全量数据进⼀步增加, 只要再增加更多的分⽚即可解决。


2 🍑数据分片算法🍑

Redis cluster 的核⼼思路是⽤多组机器来存数据的每个部分。那么接下来的核⼼问题就是,给定⼀个数据 (⼀个具体的 key),那么这个数据应该存储在哪个分⽚上? 读取的时候⼜应该去哪个分⽚读取?
围绕这个问题, 业界有三种⽐较主流的实现⽅式。

2.1 🍎哈希求余🍎

设有 N 个分⽚, 使⽤ [0, N-1] 这样序号进⾏编号,针对某个给定的 key, 先计算 hash 值, 再把得到的结果 % N,得到的结果即为分⽚编号。

后续如果要取某个 key 的记录, 也是针对 key 计算 hash , 再对 N 求余, 就可以找到对应的分⽚编号了。

  • 优点: 简单⾼效,数据分配均匀。
  • 缺点: ⼀旦需要进⾏扩容,N 改变了, 原有的映射规则被破坏, 就需要让节点之间的数据相互传输, 重新排列, 以满⾜新的映射规则。此时需要搬运的数据量是⽐较多的, 开销较⼤。

那么还有什么办法可以解决吗?

2.2 🍎⼀致性哈希算法🍎

为了降低上述的搬运开销, 能够更⾼效扩容, 业界提出了 “⼀致性哈希算法”。key 映射到分⽚序号的过程不再是简单求余了, ⽽是改成以下过程:

  • 1️⃣把 0 ~ 232-1 这个数据空间,映射到⼀个圆环上,数据按照顺时针⽅向增⻓。

redis 双机房集群 redis集群几台机器合适_数据库_02

  • 2️⃣假设当前存在三个分⽚, 就把分⽚放到圆环的某个位置上。
  • 3️⃣假定有⼀个 key计算得到 hash 值 H,那么这个 key 映射到哪个分⽚呢? 规则很简单, 就是从 H所在位置顺时针往下找,找到的第⼀个分⽚即为该 key 所从属的分⽚。
    这就相当于N 个分⽚的位置,把整个圆环分成了 N 个管辖区间。 Key 的 hash 值落在某个区间内, 就归对应区间管理。

redis 双机房集群 redis集群几台机器合适_数据_03

在这个情况下, 如果扩容⼀个分⽚, 如何处理呢?

原有分⽚在环上的位置不动, 只要在环上新安排⼀个分⽚位置即可。

redis 双机房集群 redis集群几台机器合适_缓存_04


此时只需要把 0 号分⽚上的部分数据, 搬运给 3 号分⽚即可。1 号分⽚和 2 号分⽚管理的区间都是不变的。

  • 优点: ⼤⼤降低了扩容时数据搬运的规模,提⾼了扩容操作的效率。
  • 缺点: 数据分配不均匀 (有的多有的少, 数据倾斜)

2.3 🍎哈希槽分区算法🍎

为了解决上述问题 (搬运成本⾼和数据分配不均匀),Redis cluster 引⼊了哈希槽 (hash slots) 算法。

hash_slot = crc16(key) % 16384

其中 crc16 也是⼀种 hash 算法,16384 其实是 16 * 1024, 也就是 214

相当于是把整个哈希值, 映射到 16384 个槽位上, 也就是 [0, 16383],然后再把这些槽位⽐较均匀的分配给每个分⽚,每个分⽚的节点都需要记录⾃⼰持有哪些分⽚。

假设当前有三个分⽚, ⼀种可能的分配⽅式:

  • 0 号分⽚: [0, 5461], 共 5462 个槽位
  • 1 号分⽚: [5462, 10923], 共 5462 个槽位
  • 2 号分⽚: [10924, 16383], 共 5460 个槽位

注意:

这⾥的分⽚规则是很灵活的,每个分⽚持有的槽位也不⼀定连续。每个分⽚的节点使⽤ 位图 来表⽰⾃⼰持有哪些槽位,对于 16384 个槽位来说, 需要 2048 个字节(2KB) ⼤⼩的内存空间表⽰。

如果需要进⾏扩容,⽐如新增⼀个 3 号分⽚, 就可以针对原有的槽位进⾏重新分配,⽐如可以把之前每个分⽚持有的槽位, 各拿出⼀点, 分给新分⽚。
⼀种可能的分配⽅式:

  • 0 号分⽚: [0, 4095], 共 4096 个槽位
  • 1 号分⽚: [5462, 9557], 共 4096 个槽位
  • 2 号分⽚: [10924, 15019], 共 4096 个槽位
  • 3 号分⽚: [4096, 5461] + [9558, 10923] + [15019, 16383], 共 4096 个槽位

注意:

我们在实际使⽤ Redis 集群分⽚的时候,不需要⼿动指定哪些槽位分配给某个分⽚,只需要告诉某个分⽚应该持有多少个槽位即可, Redis 会⾃动完成后续的槽位分配, 以及对应的 key 搬运的⼯作。


3 🍑面试高频出现问题🍑

3.1 🍎Redis 集群是最多有 16384 个分片吗?🍎

并⾮如此。如果⼀个分⽚只有⼀个槽位, 这对于集群的数据均匀其实是难以保证的。实际上 Redis 的作者建议集群分⽚数不应该超过1000。⽽且, 16384 这么⼤规模的集群, 本⾝的可⽤性也是⼀个⼤问题,⼀个系统越复杂, 出现故障的概率是越⾼的。

3.2 🍎为什么是 16384 个槽位?🍎

  • 节点之间通过⼼跳包通信,而⼼跳包中包含了该节点持有哪些 slots。 slots是使⽤位图这样的数据结构表⽰的,表⽰ 16384个 slots, 需要的位图大小是 2KB。如果给定的 slots 数更多了, ⽐如 65536个了, 此时就需要消耗更多的空间,在频繁的⽹络⼼跳包中, 还是⼀个不⼩的开销的。
  • 另⼀⽅⾯, Redis 集群⼀般不建议超过 1000 个分⽚,所以 16384个slots 对于最⼤ 1000 个分⽚来说是⾜够⽤的。同时也会使对应的槽位配置位图体积不⾄于很⼤。