在 Redis 集群中,数据分区是通过将数据分散到不同的节点来实现的,常见的数据分区规则有三种:节点取余分区、一致性哈希分区、虚拟槽分区。
说说节点取余分区
节点取余分区是一种简单的分区策略,其中数据项通过对某个值(通常是键的哈希值)进行取余操作来分配到不同的节点。
类似 HashMap 中的取余操作,数据项的键经过哈希函数计算后,对节点数量取余,然后将数据项分配到余数对应的节点上。
缺点是扩缩容时,大多数数据需要重新分配,因为节点总数的改变会影响取余结果,这可能导致大量数据迁移。
说说一致性哈希分区
一致性哈希分区的原理是:将哈希值空间组织成一个环,数据项和节点都映射到这个环上。数据项由其哈希值直接映射到环上,然后顺时针分配到遇到的第一个节点。
从而来减少节点变动时数据迁移的量。
Key 1 和 Key 2 会落入到 Node 1 中,Key 3、Key 4 会落入到 Node 2 中,Key 5 落入到 Node 3 中,Key 6 落入到 Node 4 中。
这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响。
但它还是存在问题:
- 节点在圆环上分布不平均,会造成部分缓存节点的压力较大
- 当某个节点故障时,这个节点所要承担的所有访问都会被顺移到另一个节点上,会对后面这个节点造成压力。
说说虚拟槽分区?
在虚拟槽(也叫哈希槽)分区中,槽位的数量是固定的(例如 Redis Cluster 有 16384 个槽),每个键通过哈希算法(比如 CRC16)映射到这些槽上,每个集群节点负责管理一定范围内的槽。
这种分区可以灵活地将槽(以及槽中的数据)从一个节点迁移到另一个节点,从而实现平滑扩容和缩容;数据分布也更加均匀,Redis Cluster 采用的正是这种分区方式。
假设系统中有 4 个实际节点,假设为其分配了 16 个槽(0-15);
- 槽 0-3 位于节点 node1;
- 槽 4-7 位于节点 node2;
- 槽 8-11 位于节点 node3;
- 槽 12-15 位于节点 node4。
如果此时删除 node2
,只需要将槽 4-7 重新分配即可,例如将槽 4-5 分配给 node1
,槽 6 分配给 node3
,槽 7 分配给 node4
,数据在节点上的分布仍然较为均衡。
如果此时增加 node5,也只需要将一部分槽分配给 node5 即可,比如说将槽 3、槽 7、槽 11、槽 15 迁移给 node5,节点上的其他槽位保留。
当然了,这取决于 CRC16(key) % 槽的个数
的具体结果。因为在 Redis Cluster 中,槽的个数刚好是 2 的 14 次方,这和 HashMap 中数组的长度必须是 2 的幂次方有着异曲同工之妙。
它能保证扩容后,大部分数据停留在扩容前的位置,只有少部分数据需要迁移到新的槽上。