分片策略详解:范围分片、哈希分片与一致性哈希

分片策略详解:范围分片、哈希分片与一致性哈希

为什么要选分片策略

分片策略决定了数据如何分布到不同的数据库或表中。不同的策略在数据分布均匀性可扩展性查询效率上有不同的表现。

一、范围分片(Range Sharding)

原理

按分片键的取值范围划分数据:

// 按 user_id 范围分片
user_id 1-1000   0
user_id 1000-2000   1
user_id 2000-3000   2
...

实现方式

-- 以 user_id 按范围分表
-- 应用层或中间件根据值判断走哪张表
String getTableName(long userId) {
    if (userId <= 10_000_000) return "user_0";
    if (userId <= 20_000_000) return "user_1";
    if (userId <= 30_000_000) return "user_2";
    return "user_3";
}

优点

  • 实现简单,路由逻辑直观
  • 范围查询效率高:WHERE id BETWEEN 100 AND 200 大概率落在同一分片
  • 扩容方便:数据量到上限时,新增分片,将新数据写入新分片即可

缺点

  • 数据分布可能不均衡:某些范围的数据量远大于其他范围
  • 热点问题:按时间分片时,新数据的写入集中在最后的分片

二、哈希分片(Hash Sharding)

原理

对分片键计算哈希值,再对分片数取模:

int shardId = hash(shardKey) % shardCount;

实现方式

// 基于 user_id 哈希分片
int shardId = user_id % 4;  // 4 个分片
String dbName = "order_db_" + shardId;

// 或者更好的哈希函数
int shardId = Math.abs(user_id.hashCode()) % shardCount;

优点

  • 数据分布均匀:哈希值的随机性保证了各分片数据量大致相等
  • 避免热点:写入请求均匀分散到各分片
  • 查询效率稳定:无论哪个分片,单表数据量相当

缺点

  • 范围查询困难:相邻 ID 的数据分散在不同分片,需要查询所有分片后合并
  • 扩缩容困难:分片数变化后,取模基数改变,大量数据需要迁移

三、一致性哈希(Consistent Hash)

原理

一致性哈希将分片键的哈希值映射到一个虚拟环上,数据按顺时针方向存储到最近的分片节点。

         NodeA
    ┌───────────┐
   ╱             ╲
NodeD              NodeB
   ╲             ╱
    └───────────┘
         NodeC

解决扩容问题的关键

传统取模分片扩容时,$新分片数 \neq 旧分片数$,导致几乎所有数据都需要重新分布。一致性哈希加入一个分片时,只需要迁移邻近分片的数据

加入 NodeE 前:
hash(user_1) → NodeA
hash(user_2) → NodeB
hash(user_3) → NodeC

加入 NodeE 后(在 NodeA 和 NodeB 之间):
hash(user_1) → NodeA  (不变)
hash(user_2) → NodeE  (从 NodeB 迁移到 NodeE)
hash(user_3) → NodeC  (不变)

虚拟节点

为了解决物理节点在环上分布不均匀(数据倾斜)的问题,一致性哈希引入虚拟节点——每个物理节点在环上对应多个虚拟节点:

物理节点:NodeA, NodeB, NodeC
虚拟节点:A1, A2, A3, B1, B2, B3, C1, C2, C3

每个物理节点对应多个虚拟节点,均匀分布在哈希环上,数据分布更均衡。当某个物理节点下线时,它的虚拟节点分散到其他节点,负载平摊。

三种策略对比

维度 范围分片 哈希分片 一致性哈希
数据均匀性 可能倾斜 均匀 均匀(虚拟节点加持)
范围查询 ✅ 高效 ❌ 需要广播 ❌ 需要广播
扩缩容 ✅ 方便 ❌ 需要迁移 ✅ 少量迁移
实现复杂度 简单 简单 较高
热点问题
跨分片合并 较少 较多 较多

实际应用中的混合策略

很多分库分表中间件支持混合策略:

策略:先用 hash 分库,再用 range 分表
db = hash(user_id) % db_count
table = (user_id / range_size) % table_count_per_db
策略:时间维度 + 用户维度
db = hash(user_id) % db_count     -- 用户维度
table = date_to_month_range(date)  -- 时间维度

面试要点

  • 理解每种策略的核心原理和适用场景
  • 重点对比:范围分片适合范围查询、哈希分片适合写入均匀、一致性哈希适合动态扩缩容
  • 一致性哈希的虚拟节点为什么能解决数据倾斜
  • 在面试中能根据业务场景推荐分片策略并说明理由
  • 扩缩容是分片策略选择中最重要的考量因素之一
© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容