当前位置: 首页 > news >正文

一致性哈希详解:优雅地扩展分布式系统

引言

对于哈希算法,相信大家一定不会陌生。它经常被用在负载均衡、分库分表等场景中。例如,在进行分库分表时,我们可能初步根据业务分析,确定 128 张表足以满足当前的数据量需求。此时,当需要插入或查询一条记录时,我们会先对分表键(如 buyer_id​)进行哈希运算,然后将运算结果对 128 取模(hash(buyer_id) % 128​)。这样就能得到一个 0 到 127 之间的数字,从而唯一定位到一个分表。

然而,随着业务的扩展,128 张表可能变得捉襟见肘。这时就需要增加分表的数量,比如增加到 129 张。如果仍然采用简单的哈希取模方式(hash(buyer_id) % 129​),问题就来了:取模的基数变了,几乎所有 buyer_id​ 计算出的目标表编号都会发生改变!这意味着几乎所有现存的数据都需要在新的 129 张表之间重新进行分配。这无疑是一项成本极高且极具破坏性的操作。

一致性哈希(Consistent Hashing) 算法正是为了解决这类问题而生的。它是一种在分布式系统中添加或删除节点时,能够有效地、尽可能地减少数据迁移和重新分布成本的算法。

一致性哈希算法原理

一致性哈希的核心思想是将数据和节点都映射到一个概念性的“哈希环”上。

  1. 构造哈希环: 首先,想象一个闭合的环,代表所有可能的哈希输出值。通常这个范围是 (0) 到 (2^{32} - 1)。这个环是整个算法的基础。

  2. 映射节点(表): 接下来,我们将系统中的每个节点(在我们的例子中是 table_0000​ 到 table_0127​ 这 128 张表)映射到哈希环上的一个位置。这通过计算节点标识符(如表名)的哈希值,然后对 (2^{32}) 取模来完成:

    • ​Hash("table_0000") % 2^32​
    • ​Hash("table_0001") % 2^32​
    • ...
    • ​Hash("table_0127") % 2^32​
      这些哈希值决定了每张表在环上的“固定”位置。
  3. 映射数据: 当需要存储数据时(例如,一条带有特定 buyer_id​ 的记录),我们同样对数据的键(buyer_id​)进行哈希运算,并将其映射到环上:

    • ​Hash(buyer_id_12321) % 2^32​
    • ​Hash(buyer_id_34432) % 2^32​
    • ...
    • ​Hash(buyer_id_767676) % 2^32​
      这样,哈希环上就同时分布了节点(表)和数据的位置。
  4. 数据归属确定: 现在,数据的位置和表的位置都在环上确定了。那么,一个特定的数据项应该存储在哪张表中呢?规则很简单:从数据在环上的位置开始,沿着顺时针方向查找,遇到的第一个节点(表),就是负责存储该数据的节点。

由于数据键(buyer_id​)和表名(table_xxxx​)的哈希值是固定的,在节点(表)数量不变的情况下,每次对同一个 buyer_id​ 进行计算,总能顺时针找到相同的目标表。

一致性哈希在节点扩容中的应用

以上就是一致性哈希的基本原理。现在回到开头的问题:如果我们需要增加一张分表(比如 table_0128​),该如何处理?

  1. 映射新节点: 首先,像其他表一样,将新表 table_0128​ 通过哈希函数映射到哈希环上:
    ​Hash("table_0128") % 2^32​
  2. 数据迁移: 新表 table_0128​ 加入后,它会“插入”到环上的某个位置。根据“顺时针查找第一个节点”的规则,原本应该存储在 table_0128​ 顺时针方向下一个节点上的一部分数据,现在其顺时针遇到的第一个节点变成了 table_0128​。因此,只有这部分数据需要从原来的节点迁移到新的 table_0128​ 中。

一致性哈希节点增加示意图

(这里可以想象一张图,显示新节点插入环中,只有其逆时针方向到上一个节点之间的数据需要迁移)

关键优势: 与普通哈希取模在增加节点时几乎需要移动所有数据不同,一致性哈希算法确保了在增加(或删除)节点后,只有受新节点位置直接影响的一小部分数据需要迁移,绝大多数数据的位置保持不变。这极大地降低了系统扩容或缩容时的成本和风险。

一致性哈希算法总结

一致性哈希通过将整个哈希空间视为一个环形结构,并将节点(服务器/表)和数据(键)都映射到这个环上。通过计算哈希值确定位置,然后依据顺时针(或逆时针)查找最近节点的规则来确定数据的归属。

优点:

  1. 最小化数据迁移: 在增加或删除节点时,仅影响环上相邻节点之间的一小部分数据,保持了较好的数据均衡性和稳定性。
  2. 高扩展性: 节点数量变化时,对整体数据结构和绝大多数数据的存储位置没有影响,易于扩展。

缺点:

  1. 哈希倾斜 (Hash Skew): 在节点数量较少,或者节点哈希值在环上分布不均匀时,可能导致某些节点负载远超其他节点,即数据倾斜。
  2. 雪崩效应(级联故障): 如果一个节点失效,其负载会完全转移给顺时针的下一个节点,可能导致该节点过载而失效,引发连锁反应。(引入虚拟节点可以缓解)
  3. 节点频繁变更的开销: 虽然比普通哈希好很多,但如果节点添加或删除非常频繁,仍然会带来不可忽视的数据迁移开销和系统压力。

Hash 倾斜的解决方法

正如缺点中提到的,即使使用一致性哈希,如果节点在环上分布不均,或者某些数据哈希后天然地聚集在一起,仍然可能出现数据倾斜问题——即少数节点承载了大量数据。这会导致节点负载不均,并且在这些“胖”节点发生变更(如宕机或扩容)时,数据迁移量依然较大。

解决方法主要有两种思路:

  1. 增加物理节点数量: 在服务器资源充足的情况下,可以部署更多的物理节点。节点越多,理论上每个节点分担的数据量越少,即使有倾斜,单个节点的压力也可能降低到可接受范围。但这并不能完全解决哈希分布不均的根本问题,且成本较高。

  2. 引入虚拟节点 (Virtual Nodes): 这是更常用且更有效的方法。与其将一个物理节点(如一台服务器或一张表)只映射到环上的一个点,不如将其映射为环上的多个虚拟节点。

    • 例如,一个物理节点 Node A​ 可以映射为 Node A-1​, Node A-2​, ..., Node A-k​ 这些虚拟节点,每个虚拟节点通过不同的哈希计算(比如 hash("Node A-1")​, hash("Node A-2")​)得到在环上的不同位置。
    • 数据映射时,仍然是顺时针查找第一个虚拟节点。
    • 当一个物理节点加入或离开时,它相当于同时加入或移除了它所对应的所有虚拟节点。
    • 好处: 大量的虚拟节点通常能在环上分布得更均匀,即使物理节点数量不多,也能有效打散数据,显著改善数据倾斜问题。同时,当物理节点增删时,负载能更均匀地分散给其他多个物理节点(通过它们各自的虚拟节点),而不是仅仅压垮顺时针的下一个邻居。

通过引入虚拟节点,一致性哈希算法能够更好地应对数据倾斜,进一步提高系统的负载均衡能力和稳定性。


http://www.xdnf.cn/news/162343.html

相关文章:

  • 反爬加密字体替换机制解析
  • HBase协处理器深度解析:原理、实现与最佳实践
  • 【Qt】信号与槽:构建灵活交互的核心机制
  • JAVAEE初阶01
  • 数据安全和合规性市场分析
  • MES系列-MOM(Manufacturing Operations Management,制造运营管理)
  • Redis为什么不直接使用C语言中的字符串?
  • Eigen迭代求解器类
  • async 和 await 详解
  • 论文阅读:2025 arxiv Aligning to What? Limits to RLHF Based Alignment
  • Lustre/Scade/Swan 语义性质中的因果性分析介绍
  • ES6 Map/WeakMap/Set/WeakSet 全解指南
  • 2软考系统架构设计师:第一章系统架构概述 - 练习题附答案及超详细解析
  • 直接映射例题及解析
  • 大模型微调与蒸馏的差异性与相似性分析
  • 字节跳动开源数字人模型latentsync1.5,性能、质量进一步优化~
  • 1.1.1 用于排序规则的IComparable接口使用介绍
  • 【MinIO实战】MinIO权限策略设置与上传文件时报错Access Denied排查
  • 03.01、三合一
  • CentOS7 部署 Ollama 全栈指南:构建安全远程大模型服务
  • 【Python】Python中的浅拷贝和深拷贝
  • Halcon算子应用和技巧13
  • Spring AI Alibaba - Milvus 初体验,实现知识库效果
  • SDC命令详解:使用reset_design命令重置设计
  • 力扣热题100题解(c++)—链表
  • Python项目实践:控制台银行系统与词频统计工具开发指南
  • c#简易超市充值卡程序充值消费查余额
  • 升级 Spring Boot CLI
  • 信用中国【国密SM2、SM4加解密】逆向算法分析
  • 【学习笔记】Stata