人人都会AI编程

27.2 分片算法:哈希分片、范围分片、一致性哈希

更新时间:2026-07-11

分片算法本质上回答了同一个问题:“新写入的这一行数据,应该落到哪个分片库/分片表里?” 如果这个规则定得不好,轻则数据分布不均可导致热点,重则一扩容整个集群就得大规模迁移,弄得线上服务长时间抖动。

在实际项目中,分片算法通常由中间件(如 ShardingSphere、MyCat)在配置中指定,你的应用程序不需要手动计算路由。但理解其算法原理、各自优劣与适用场景,才能设计出真正适应业务增长的拆分方案。

27.2.1 范围分片:简单直观,但热度难控制

范围分片是最自然的分片方式:根据分片键的连续区间,将数据划分到不同的库或表。比如按时间、按 ID 区间等。

示例:

假设用 user_id 做分片键,范围为 1~1000 万的用户数据:

  • 分片 0:user_id 在 [1, 2000000]
  • 分片 1:user_id 在 [2000001, 4000000]
  • 分片 4:user_id 在 [8000001, 10000000]

建表时可以用类似 CREATE TABLE user_001 / user_002 的方式命名,中间件根据 user_id 落在哪个区间路由到对应物理表。

优点:

  • 实现清晰,查 WHERE user_id BETWEEN ? AND ? 这类范围查询可以直接精准定位到特定分片,性能高效。
  • 数据可预见地按范围增长,扩容时只需新增后续区间,不必立即搬动老数据。
  • 对于按时间范围(如按订单创建月)分片,天然适合归档与清理历史数据。

缺点:

  • 热点问题:如果数据分布与访问热度不匹配(例如用户大多是近期注册的,集中在较大 ID 区间),新分片会承受绝大部分读写压力,老分片反而闲置。这对于新老用户比例悬殊的业务往往是致命的。
  • 分片键选择困难:如果分片键不是单调递增或业务上不存在连续范围,数据分布可能严重倾斜。
  • 后续扩容需要“裂分”:当原有分片上限被突破,重新划分范围可能需要迁移部分数据,操作比哈希分片更复杂。

适用场景:

适合按自然递增且访问热度均匀的键来拆分,例如日志类表按时间月份分表、流水数据按固定时间窗口归档。对于严格按范围的查询较多、数据冷热分层明显的场景,范围分片是不错的选择。但如果业务有明显的热点分布,最好避免用单纯的区间划分。

27.2.2 哈希分片:均匀分拆,但扩容迁移量巨大

哈希分片通过一个哈希函数对分片键进行计算,然后按结果对分片总数取模,将数据离散地打散到各个分片

示例:

假设将用户表水平拆分为 8 个库,每个库再分为 4 张表(共 32 个逻辑分片),分片键为 user_id

分片序号 = hash(user_id) % 32

根据 分片序号 路由到具体的物理表(如 db_03.user_01)。

优点:

  • 数据分布极其均匀:只要分片键的取值足够随机,数据量基本平均分配,不存在明显冷热不均。
  • 对于增删改查请求,热点行会被分散在不同分片,避免单分片压力过大。
  • 实现简单,很多分库分表中间件开箱即支持取模分片。

缺点:

  • 扩容几乎等于重新洗牌:当分片数从 32 变成 64 时,几乎全部数据的哈希结果都会改变,需要大规模数据迁移。即便逻辑上只增加新节点,也会引发大量数据搬移,这极容易影响线上服务。
  • 范围查询失效WHERE user_id BETWEEN 1000 AND 2000 无法确定数据落在哪个分片,需要广播到所有分片查询后归并结果,性能随着分片数增加而线性下降。
  • 分片键选择受限:由于必须参与所有查询,通常要求分片键为查询条件中的必备字段,否则全分片扫描在所难免。

怎么缓解?

在实践中,为了减缓扩容的痛苦,常用技巧有两种:

  • 预分片:提前规划充分大的分片数(比如 128 或 256),初期这些分片逻辑上存在但物理上落在较少的数据库实例上。未来扩容时只需迁移部分分片(如把分片 65-128 移到新机器),不改变哈希规则,避免全量迁移。
  • 一致性哈希(见下一节)就是在经典取模方式基础上的重大改良,彻底缓解了大范围数据搬迁问题。

适用场景:

对于数据写入量大、读写压力需要充分均匀散布的场景(如用户中心、账户系统),哈希分片配合预分片策略是非常成熟的实用方案。只要你能接受范围查询功能的削弱,并且提前做好未来扩容的规划(例如使用 ShardingSphere 的分片策略与虚拟分片),它就能提供非常优秀的分摊能力。

27.2.3 一致性哈希:面向弹性伸缩的改良

一致性哈希最初由分布式缓存(如 Memcached)推广开来,在数据库分片领域同样大放异彩。它的核心思想是:将分片节点和数据都映射到一个环上,数据通过顺时针方向找到第一个节点作为归属。当新增或移除节点时,只有环上相邻的一小段数据会受影响,而不是全量重新哈希。

基本模型:

  1. 构造一个范围在 [0, 2^32 - 1] 的环。
  2. 将每个物理节点通过哈希函数(通常结合可配置的虚拟节点数)映射到环上的多个点。
  3. 每条数据同样计算一个哈希值落在环上,然后顺时针寻找第一个节点,该节点就是数据归属的物理分片。

虚拟节点的重要性:

如果只有少数几个物理节点直接映射在环上,数据分布极不均匀,个别节点负载过重。引入虚拟节点后,一个物理节点在环上对应多个位置(比如 150 个虚拟节点),数据的分布就变得平滑均匀,同时仍保有迁移量低的优势。

优点:

  • 动态伸缩迁移量极小:当分片集群增加节点时,仅需要移动相邻节点的一部分数据,远小于取模哈希的全量搬迁。在业务高峰期进行在线扩容,风险大大降低。
  • 分布相对均匀:配合足够多的虚拟节点,数据分片可以近似均匀。
  • 节点角色灵活:既可以手动指定每个节点权重(通过虚拟节点数),也可以动态上下线。

缺点:

  • 实现较复杂:哈希环的维护、虚拟节点映射、数据迁移工具都需要成熟的中间件支持。好在 ShardingSphere 现已经内置“基于一致性哈希的分片算法”,可以直接声明使用。
  • 范围查询依然需要广播:与取模哈希类似,一致性哈希本质上还是哈希分片,不支持跨分片的范围查找。
  • 可能存在轻微数据倾斜:如果节点数量很少且未配置足够的虚拟节点,仍可能出现偏差,需要测试验证。

实际应用示例:

在 ShardingSphere 中,可以这样定义一个一致性哈希的分片策略:

shardingAlgorithms:
  order-consistent-hash:
    type: CONSISTENT_HASH
    props:
      hash-algorithm-name: MD5    # 哈希算法
      virtual-nodes: 160          # 每个物理节点的虚拟节点数

当需要扩容时,你只需在配置中加入新的数据源,ShardingSphere 会自动根据一致性哈希算法将部分数据路由到新节点,同时支持在线迁移。

适用场景:

非常适合业务处于快速增长期、未来分片数会频繁增加的系统。例如电商订单表、物流单号表,数据量膨胀速度不可预知,且无法接受大规模停服迁移。一致性哈希结合预分片思想,在关系型数据库分库分表领域已得到充分验证,成为很多大中型系统的标准配置。

27.2.4 三种分片算法对比与选择指南

| 维度 | 范围分片 | 哈希分片(取模) | 一致性哈希 |
|------|----------|----------------|------------|
| 数据分布 | 不均匀,容易冷热不均 | 非常均匀 | 较均匀(依赖虚拟节点) |
| 扩容迁移量 | 新增范围可能需裂分,迁移局部 | 全量重新哈希,迁移量大 | 仅迁移相邻节点部分数据 |
| 范围查询 | 精准定位,效率高 | 广播查询,性能随分片数下降 | 广播查询 |
| 实现复杂度 | 低 | 低 | 中等(中间件已封装) |
| 热点应对 | 弱,热点分片风险 | 强,均匀分布 | 强,均匀分布 |
| 典型场景 | 按时间分表归档,日志系统 | 用户表、账户表等需要均匀分布的场景 | 分片数可能频繁变动的核心业务表 |

选择建议:

  1. 如果你的查询模式天然依赖范围条件,并且数据冷热分界明显(比如按时间),那么优先考虑范围分片;但要确认业务不会在某个分区出现热点。
  2. 如果追求绝对的负载均匀,并且可以接受未来一次性分片数固定(或者用预分片规避扩容),经典取模哈希是简单可靠的选择。
  3. 如果分片数将来一定还会加,且不能接受大规模数据搬迁的停服时间,那么直接用一致性哈希。在中间件成熟的今天,它的运维成本已经降低到可接受范围。

实际上,很多大项目会采用组合分片策略:先按范围(如年月)做第一层分表,再在每张表中用哈希分片进一步打散写入压力。你可以在 ShardingSphere 的复合分片策略里自由搭配,最终目的只有一个:让数据访问尽量落在一个分片上,同时确保分片间的数据量与访问量偏差可控。