1.数据分布简介
(1).分布式数据库的数据分区
随着请求量和数据量的增加,一台机器已经无法满足需求,就需要将数据和请求分散到多台机器,这时候就需要引入分布式存储。分布式存储首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。
(2).常见的分区规则
- 节点取余分区
- 一致性哈希分区
- 哈希槽分区(Redis Cluster采用的分区规则)
2.节点取余分区
(1).计算公式
节点取余分区使用hash(key)%nodes(使用特定的数据,如Redis的键作为key和节点数量nodes)来计算哈希值,以决定数据映射到哪一个节点上。
(2).存在的问题
当节点数量变化时,如扩容或收缩节点,数据节点映射关系需要重新计算,会导致数据的重新迁移,其示意图如下所示。
(3).多倍扩容
(4).总结
- 客户端分片:哈希 + 取余
- 节点伸缩:数据节点关系变化,导致数据迁移
- 迁移数量和添加节点数量有关:建议翻倍扩容(一般不建议使用,因为这样会降低性能,增加底层数据源的访问压力)
3.一致性哈希分区
(1).什么是一致性哈希
每个节点分配一个token(范围在0~2的32次方),这些token构成一个哈希环。数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点。
Redis集群采用哈希槽(slot)的分区算法,并没有采用一致性哈希分区算法,主要原因是一致性哈希算法对于数据分布和节点位置的控制并不是很好。
(2).扩容
这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其它节点无影响,其数据漂移示意图如下所示。
(3).存在的问题
加减节点会造成哈希环中部分数据无法命中,如之前n1-n2之间的4个数据,由于n5节点的加入,当应用读取n5-n2之间的数据时,Redis会先去n5获取,获取不到就只能去存储层去获取,然后回写到n2节点。当使用少量节点时,节点变化将大范围影响哈希环中数据映射,因此这种方式不适合少量数据节点的分布式方案。
(4).总结
- 客户端分片:哈希 + 顺时针
- 节点伸缩:只影响邻近节点,但还是有数据迁移
- 翻倍伸缩:保证最小迁移数据和负载均衡
4.哈希槽分区
(1).简介
Redis集群没有单机的那种16个数据库的概念,而是分成了16384个槽,每个节点负责其中一部分槽位,槽位的信息存储于每个节点中。
注意,对于槽位的转移和分派,Redis集群不会自动进行,而是人工配置的,所以Redis集群的高可用依赖于节点的主从复制与主从间的自动故障转移。
(2).槽范围
槽范围是0~16383,假如现在集群有5个节点,那么每个节点平均大约负责3277个槽。
为什么是16384个槽?
1.如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
2.redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
3.槽位越小,节点少的情况下,压缩比高。
Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots/N很高的话(N表示节点数),bitmap的压缩率就很低。如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
(3).哈希槽分配算法
Redis Cluser采用哈希槽分区,所有的键根据哈希函数映射到0~16383内,计算公式为slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。
(4).总结
- 解耦数据和节点之间的关系,简化了节点扩容和收缩难度。
- 节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据。
- 支持节点、槽、键之间的映射查询,用于数据路由、在线伸缩等场景。