引言
对于哈希算法,相信大家一定不会很陌生,其经常被用在负载均衡、分库分表等场景中,比如我们在做分库分表的时候,最开始根据业务分析,只需要 128 张表就可以满足数据量的要求了,但是是这个时候如果你要插入或者查询一条记录的时候,我们就会先把分表键,如 buyer_id 进行 Hash 运算,然后将运算结果对 128 取模,最后就可以得到 0 -127 之间的数字,这样就可以唯一定位到一个分表。
但是随着业务的扩展,128 张表可能已经不够用了,这个时候就需要重新分表了,比如增加一张新的表,这个时候如果采用 Hash 或者取模的方式,就会导致 128 + 1 张表的数据需要重新进行分配,成本非常得高。
而一致性哈希算法就是专门解决这类问题的算法,其可以有效地解决分布式系统中增加或者删除节点时的失效问题。
一致性哈希(Consistent Hashing)是一种用于分布式系统中数据分片和负载均衡的算法。它的目标是在节点的动态增加或者删除的时候,尽可能地减少数据迁移和重新分布的成本。
一致性哈希算法原理
实现一致性哈希算法首先需要构造一个哈希环,然后把他划分为固定数量的虚拟节点,一般都是 。那么他的节点编号就是,如下图所示:
接下来,我们将 128 张表作为节点映射到这些虚拟节点上,每个节点在哈希环上面都有一个对应的虚拟节点:
Hash(table_0000) % 、Hash(table_0001)% 、Hash(table_0002)% 、········
Hash(table_0127) % (如下图所示,将哈希后的虚拟节点分布到哈希环上)
然后将这些表做好 Hash 映射之后,我们就需要存储数据了,现在我们要把一些需要分表的数据也根据同样的算法进行 Hash,并且也将其映射到哈希环上面。
Hash(buyer_id) %
Hash(12321)% 、Hash(34432)% 、Hash(54543)% 、········ Hash(767676) %
经过以上步骤,在这个 Hash 环上面的虚拟节点就包含两部分数据的映射了,一部分是存储数据的分表的映射,一部分是真实要存储的数据的映射。
现在我们思考一个问题,我们最终目的是将这些数据存储到数据库分表中,那么做好哈希之后,这些数据又要保存在哪个数据库表节点中呢?
这个其实很简单就,就是按照数据的位置,沿着顺时针的方向进行查找,找到的第一个接地就是数据要存放的数据库表节点:
因为要存储的数据,以及存储这些数据的数据库分表, Hash 后的值都是固定的,所以在数据库数量不变的情况下,下次想要进行分数据插入或者查询的时候,只需要按照同样的算法去计算一次就能找到对应的分表了。
以上,就是一致性 Hash 算法的原理了,那么现在回到开头那个问题,如果我们要增加一个分表要怎么进行处理?
接下来我们来讨论一下:
一致性 Hash 算法在节点扩容中的应用
我们首先将要增加的表通过一致性 Hash 算法映射到哈希环的虚拟节点中:
这样的话,就会有一部分数据因为节点数量的改变,其顺时针第一个遇到的分表也随之发生了改变。
相比于普通的 Hash 算法,一致性 Hash 算法可以做到在增加服务器之后,影响到的数据范围最小,只影响小范围的数据,然后其他的数据都不需要进行调整。
以上就是一致性哈希算法的原理以及骑在节点扩容中的应用了,现在我们来进行一个小总结。
一致性哈希算法总结
一致性哈希算法其就是将整个哈希空间视为一个环形结构,然后将节点和数据都映射到这个哈希环上面。每一个节点通过计算节点的哈希值,将节点映射到哈希环上的一个位置。然后数据也通过计算出来的哈希值,将数据映射到哈希环上的一个位置。
当有新的数据需要存储时,首先计算数据的哈希值,然后顺时针或逆时针在环上找到最近的节点,将数据存储在这个节点上。当需要查找数据时,同样计算数据的哈希值,然后顺时针或逆时针在环上找到最近的节点,从该节点获取数据。
一致性哈希算法的优缺点:
优点:
- 数据均衡:在增加或者删除节点的时候,一致性哈希算法只会影响到少量的数据迁移,保持了数据的均衡性。
- 高扩展性:当节点数目发生变化的时候,对于已经存在的数据,只有部分数据需要重新分布,不会影响到整体的数据结构。
但是,作为一个算法,其有优点的同时,一定会有他的缺点:
- Hash 倾斜:在节点数较少的情况下,由于哈希空间是有限的,节点的分布可能不够均匀,导致数据倾斜。
- 节点的频繁变更: 如果频繁添加或删除节点,可能会导致大量的数据迁移,从而造成系统压力。
Hash 倾斜的解决方法
其实,从图中可以看出, Hash 倾斜的主要问题就是如果数据过于集中,就会导致在节点数量发生变化的时候,会有大量数据发生迁移,从而导致的数据迁移的成本过高,那么对于这个问题,有什么好的解决方法吗?
答案是肯定的,如果没有的话就没有这最后一个小结了,现在我们就来探究一下解决的方法:
- 在服务器节点充足的情况下,可以最大程度地增加服务器节点,然后尽可能地分散节点,使得数据分布较为均匀。
- 那么服务器要是没那么充足,这个怎么解决,这个也比较简单,我们可以引入一些虚拟节点。即我们将一个服务器节点拆分成多个虚拟节点,然后数据在映射的时候先将数据映射到虚拟节点上,然后虚拟节点在对应的物理节点进行存储和读取就可以了,有了虚拟节点的接入,数据在分布的时候就会尽可能地分散,然后在增加或者减少服务器数量的时候,受到影响的数据范围也不会有那么多。