什么是数据分区
分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。如下图:
换句话说,redis分区就是将数据分布到不同的redis实例中,因此对于每个redis实例所存储的内容仅仅是所有内容的一个子集。
这里重点需要关注的是数据分区规则。常见的分区规则有hash分区和顺序分区两种。
分区方式 | 特点 | 代表产品 |
---|---|---|
哈希分区 | 离散度好、数据分布和业务无关、无法顺序访问 | Redis Cluster、Cassandra、Dynamo |
顺序分区 | 离散度容易倾斜、数据分布和业务有关、可顺序访问 | Bigtable、Hbase、Hypertable |
一致性哈希和哈希槽
1、集群分片模式
如果 redis 只用复制功能做主从,那么当数据量巨大的情况下,单机情况下可能已经承受不下一份数据,更不用说是主从都要各自保存一份完整的数据。在这种情况下,数据分片是一个非常好的解决办法。
redis 的 custer 正是用于解决该问题。它主要提供两个功能:
- 自动对数据分片,落到各个节点上
- 即使集群部分节点失效或者连接不上,依然可以继续处理命令
对于第二点,它的功能有点类似于 sentienl 的故障转移,在这里不细说。下面详细了解下 redis 的槽位分片原理,在此之前,先了解下分布式简单哈希算法和一致性哈希算法,以帮助理解槽位的作用。
2、简单哈希算法
假设有三台机,数据落在哪台机的算法为:
c = Hash(key) % 3
例如 key A 的哈希值为4,4 % 3 = 1,则落在第二台机。Key ABC 哈希值为11,11 % 3 = 2,则落在第三台机上。
利用这样的算法,假设现在数据量太大了,需要增加一台机器。A 原本落在第二台上,现在根据算法4 % 4 = 0,落到了第一台机器上了,但是第一台机器上根本没有 A 的值。这样的算法会导致增加机器或减少机器的时候,引起大量的缓存穿透,造成雪崩。
也就是说:当节点数量变化时,比如扩容或者收缩节点,数据节点映射关系需要重新计算,会导致数据的重新迁移
优点:
这种方案的突出优点是简单性,常用于数据库的分库分表规则,一般采用预分区的方式,提前根据数据量划分好分区数,比如划分512或者1024张表,保证可支撑未来一段时间的数据量,在根据负载情况将表迁移到其他数据库中。扩容时通常采用翻倍扩容,避免数据映射全部被打乱导致全量迁移的情况。
3、一致性哈希算法
在1997年,麻省理工学院的 Karger 等人提出了一致性哈希算法,为的就是解决分布式缓存的问题。
在一致性哈希算法中,整个哈希空间是一个虚拟圆环。
假设有四个节点 Node A、B、C、D,经过 ip 地址的哈希计算,它们的位置如下:
有4个存储对象 Object A、B、C、D,经过对 Key 的哈希计算后,它们的位置如下:
对于各个 Object,它所真正的存储位置是按顺时针找到的第一个存储节点。例如 Object A 顺时针找到的第一个节点是 Node A,所以 Node A 负责存储 Object A,Object B 存储在 Node B。
一致性哈希算法大概如此,那么它的容错性和扩展性如何呢?
假设 Node C 节点挂掉了,Object C 的存储丢失,那么它顺时针找到的最新节点是 Node D。也就是说 Node C 挂掉了,受影响仅仅包括 Node B 到 Node C 区间的数据,并且这些数据会转移到 Node D 进行存储。
同理,假设现在数据量大了,需要增加一台节点 Node X。Node X 的位置在 Node B 到 Node C 之间,那么受到影响的仅仅是 Node B 到 Node X 间的数据,它们要重新落到 Node X 上。
所以一致性哈希算法对于容错性和扩展性有非常好的支持。但一致性哈希算法也有一个严重的问题,就是数据倾斜。
如果在分片的集群中,节点太少,并且分布不均,一致性哈希算法就会出现部分节点数据太多,部分节点数据太少。也就是说无法控制节点存储数据的分配。如下图,大部分数据都在 A 上了,B 的数据比较少。
优点:
这种方式相比节点取余最大的好处至于加入和删除节点值影响hash环中相邻的节点,对其他节点无影响。
缺点:
- 加减节点会造成哈希环中部分数据无法命中,需要手动处理或者忽略这部分数据,因此一致性hash算法常用于缓存场景
- 当使用少量节点时,节点变化将大范围影响哈希环中数据映射,因此这种方式不适合少量数据节点的分布式方案
- 普通的一致性哈希分区在增减节点时需要增减/减去一半节点才能保证数据和负载的均衡
因为一致性hash算法对数据分布、节点控制不是很友好,所以redis集群(cluster)对一致性hash进行改进
PS:一致性哈希分区(如果前面没看懂,可以看看这个)
一致性哈希分区(Distributed Hash Table)实现思路是为系统中每个节点分配一个token,范围一般在0~2 32 ,这些token构成一个哈希环。数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点。如下图所示:
这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响。但一致性哈希分区存在几个问题:
加减节点会造成哈希环中部分数据无法命中,需要手动处理或者忽略这部分数据,因此一致性哈希常用于缓存场景。
当使用少量节点时,节点变化将大范围影响哈希环中数据映射,因此这种方式不适合少量数据节点的分布式方案。
普通的一致性哈希分区在增减节点时需要增加一倍或减去一半节点才能保证数据和负载的均衡。
4、哈希槽
redis 集群(cluster)并没有选用上面一致性哈希,而是采用了哈希槽(slot)的这种概念。主要的原因就是上面所说的,一致性哈希算法对于数据分布、节点位置的控制并不是很友好。
首先哈希槽其实是两个概念,第一个是哈希算法。redis cluster 的 hash 算法不是简单的 hash(),而是 CRC16 算法,一种校验算法。另外一个就是槽位的概念,空间分配的规则。其实哈希槽的本质和一致性哈希算法非常相似,不同点就是对于哈希空间的定义。一致性哈希的空间是一个圆环,节点分布是基于圆环的,无法很好的控制数据分布。而 redis cluster 的槽位空间是自定义分配的,类似于 windows 盘分区的概念。这种分区是可以自定义大小,自定义位置的。
redis cluster 包含了16384个哈希槽,每个 key 通过计算后都会落在具体一个槽位上,而这个槽位是属于哪个存储节点的,则由用户自己定义分配。例如机器硬盘小的,可以分配少一点槽位,硬盘大的可以分配多一点。如果节点硬盘都差不多则可以平均分配。所以哈希槽这种概念很好地解决了一致性哈希的弊端。
另外在容错性和扩展性上,表象与一致性哈希一样,都是对受影响的数据进行转移。而哈希槽本质上是对槽位的转移,把故障节点负责的槽位转移到其他正常的节点上。扩展节点也是一样,把其他节点上的槽位转移到新的节点上。
但一定要注意的是,对于槽位的转移和分派,redis 集群是不会自动进行的,而是需要人工配置的。所以 redis 集群的高可用是依赖于节点的主从复制与主从间的自动故障转移。
参考链接
- https://blog.youkuaiyun.com/zhizhengguan/article/details/120684047
- https://baijiahao.baidu.com/s?id=1714747347383071368&wfr=spider&for=pc