背景
随着数据量的不断增长,单个数据库已经无法支撑现有的业务时,就需要引入分布式存储。比如MySQL分库分表、Redis Cluster 集群。
引入分布式存储时,不得不面临一个问题:如何保证数据尽可能均匀分布到各个节点上,并且尽量在增减节点时较小范围对现有数据的影响。
以下以集群缓存服务器
为案例分析,分析如何实现数据均匀分布的。
常见方案
一、Hash取模
1.1 描述
根据 key 进行 Hash,然后取模分配到N个节点,则可以通过 Hash(key) % N
计算出存放的节点。
1.2 优点
可以实现数据均衡分布,简单易懂。
1.3 缺点
当有节点增加或者删除时,会造成大量数据失效。因此容错性和扩展性都比较差。
下面以3个节点减到2个节点为例子,计算一下失效率:
图中6个key,标记红色为失效的有4个,失效率=66.7%
二、一致性Hash算法
2.1 描述
首先,构建一个一致性 Hash 环的结构。一致性 Hash 环的大小是 0 到 2^32-1
。
然后,将缓存服务器的节点(如 IP 地址或 hostname)经过 Hash 算法后散列到这个环上。
如有3个节点时,分布情况如下图所示:
有3个key需要存储到缓存集群时,分别计算 Hash 后映射到这个环上。
然后,按照顺时针方向把 key1 定位到 Node1,key2 定位到 Node2,key3 定位到 Node3。
2.2 优点
2.2.1 容错性
当 Node2 宕机之后,key2 被重新定位到 Node3 节点上,此时只会影响 Node1 和 Node2 之间的数据,很好实现了容错。
2.2.2 扩展性
当缓存集群需要扩展一个 Node4 节点时,key1 被重新定位到 Node4 节点上,此时只会影响 Node3 和 Node4 之间的数据,很好实现了可扩展性。
2.3 缺点
仔细看刚刚图可以发现,节点之间的分布并不均匀。Node1 和 Node2 距离较近,分布较少的数据;而 Node2 和 Node3 距离较远,分布了较多的数据。
三、虚拟节点
为了解决一致性 Hash 出现的分布不均匀的问题,引入 虚拟节点
来解决该问题。
将每台集群节点虚拟为一组节点,如图中每个节点虚拟出2个节点(实际应用中可能有很多)。将虚拟服务器的 Hash 值放置在 Hash 环上。
当有一个 key 需要存储到缓存集群时,先找到虚拟节点,再得到物理服务器信息。
假如有新节点加入时,也按相同规则分配时,即可保证数据的均匀分布。
思考题
数据库在做分库分表,如何能做到在增加分表数时,尽可能少迁移数据呢?