1. 传统的哈希算法
假设存储集群中一共有 mmm 个节点,且已使用 0,1,..,m−10,1,..,m-10,1,..,m−1 进行编号,现要将对象 ooo 存到集群上。如果使用传统的哈希算法来确定 ooo 的存储位置,则 ooo 应存储在编号为 hash(o.id)%m\text{hash}(o.id)\%mhash(o.id)%m 的节点上。(o.ido.ido.id 表示对象 ooo 的 ID)
传统哈希算法的不足是可扩展性差,当集群增删节点时,mmm 便会改变,所有数据的存储位置都得重新计算,从而导致已有的数据发生大量的迁移。
2. 一致性哈希算法
一致性哈希算法将节点、数据的存储位置映射到了一个环上,如,范围为 [0,232)[0, 2^{32})[0,232) 的一个环空间。
假设存储集群中存在 A,B,CA, B, CA,B,C 三个节点。首先对节点的 IP 或主机名进行哈希,以将节点映射到环上,如下图所示。
可见,整个环空间被三个节点划分为三段:(C,A],(A,B],(B,C](C, A], (A, B], (B, C](C,A],(A,B],(B,C],分别由节点 A,B,CA,B,CA,B,C 负责存储落在该区间中的数据。
例如,当要存储对象 ooo 时,首先计算其存储位置:hash(o.id)%232\text{hash}(o.id)\%2^{32}hash(o.id)%232,如果 hash(o.id)%232\text{hash}(o.id)\%2^{32}hash(o.id)%232 的值落在 (C,A](C,A](C,A] 区间中,则由节点 AAA 存储 ooo,以此类推。
相比于传统的哈希算法而言,当集群发生节点增减时,使用一致性哈希算法能够减少数据迁移量。例如,如果要移除节点 CCC,则之后由节点 AAA 负责 (B,A](B,A](B,A] 区间,由 BBB 负责 (A,B](A,B](A,B] 区间,即只需要将 CCC 上的数据迁移给 AAA,而 BBB 上的数据不需要迁移。
当然,一致性哈希算法也有其缺点。如,
- 多个节点不一定能够平分整个环空间,从而导致数据分布不均。
- 当发生节点增减时,部分数据发生迁移,又会导致数据分布不均。
3. 改进的一致性哈希算法
改进的一致性哈希算法主要是为了缓解一致性哈希算法中的数据分布不均的问题。
改进的一致性哈希算法引入了虚拟节点这一层抽象,由虚拟节点平分整个环空间,再将虚拟节点映射到物理节点上。此处仍假设环空间的大小为 [0,232)[0,2^{32})[0,232)。
例如,上图中由六个虚拟节点 V1,...,V6V1,...,V6V1,...,V6 平分了整个环空间,假设存储集群中存在 A,B,CA, B, CA,B,C 三个物理节点。此时可以将虚拟节点 V1,V2V1, V2V1,V2 映射到物理节点 AAA 上,将 V3,V4V3, V4V3,V4 映射到 BBB 上,将 V5,V6V5,V6V5,V6 映射到 CCC 上。
假设 hash(o.id)%232\text{hash}(o.id)\%2^{32}hash(o.id)%232 的值落在区间 (V1,V2](V1,V2](V1,V2] 中,即由虚拟节点 V2V2V2 负责存储对象 ooo,而 V2V2V2 又被映射到了物理节点 AAA 上,所以实际上由物理节点 AAA 来负责存储对象 ooo。相比于一致性哈希算法而言,此处能够实现更加均匀的数据分布。
当集群增减物理节点导致数据迁移时,也能够使数据分布较为均匀。例如,集群需要移除物理节点 CCC,此时可以将 V5V5V5 分配给物理节点 AAA,将 V6V6V6 分配给物理节点 BBB,即 CCC 上的数据会迁移到 AAA 和 BBB 上(当然,得有数据映射到 V5V5V5 和 V6V6V6 上才行),使得 AAA 和 BBB 的负载保持均衡。