什么是数据倾斜,会带来哪些问题?如何解决?

#中间件#面试八股

什么是数据倾斜,会带来哪些问题?如何解决?

结论:数据倾斜(Data Skew)指在同一分片规则下,个别分片承载的数据量或请求量远高于平均值。它让集群从「并行」退化为「单点」,扩容也救不了——热点键永远落在同一个分片。治理只有两条路:把热点拆开(加盐、拆分维度),或把热点单独拎出来(独立库、独立缓存)。

一、倾斜的三种形态

形态表现典型例子
数据量倾斜某分片行数是均值的数倍大商户订单集中在 merchant_id % 16 == 3
请求量倾斜行数均匀但 QPS 集中爆款商品详情、热点直播间
时间倾斜新数据全落在最新分片按月范围分表,写入永远打到当月表
理想:  ds0 ████████   ds1 ████████   ds2 ████████   ds3 ████████
倾斜:  ds0 ██         ds1 ██         ds2 ██████████████████   ds3 ██
                                     └── 木桶的短板在这里

二、为什么会倾斜

  • 分片键基数太低:按 status、gender、province 分片,取值只有几个,天然无法均分。
  • 业务分布本身长尾:B 端系统里 1% 的大商户贡献 60% 的数据。
  • 哈希低位相关性:当分片数是 2 的幂时 hash & (n-1) 只用到低位,自增 ID、时间戳拼接的键低位高度相关,容易聚堆。
  • 事件驱动:大促、定时任务在同一时刻写同一批键。

三、治理手段

  1. 加盐(Salting):给热点键拼随机后缀,把一个逻辑分片拆成 N 份。
  2. 拆分维度:主表按 user_id 分片,大商户订单路由到独立库。
  3. 基因法(Gene Method):把用户 ID 的尾部比特嵌入订单号,使订单号自带路由信息。
  4. 二级索引表:按 merchant_id 建映射表,先查映射再定位物理表。
  5. 热点隔离:大客户独享实例,或走独立缓存加限流。
private static final int SALT = 8;

// 写:同一商户的订单被打散到 8 个子分片
String writeShard(long merchantId) {
    int salt = ThreadLocalRandom.current().nextInt(SALT);
    int hash = Math.floorMod((merchantId + "#" + salt).hashCode(), 64);
    return "t_order_" + hash;
}

// 读:不知道 salt 时必须扇出到所有盐位再归并
List<Order> queryAll(long merchantId) {
    List<Order> all = new ArrayList<>(64);
    for (int s = 0; s < SALT; s++) {
        int hash = Math.floorMod((merchantId + "#" + s).hashCode(), 64);
        all.addAll(orderMapper.selectByMerchant("t_order_" + hash, merchantId));
    }
    return all;
}

加盐的代价是读放大 8 倍,只适合「写多读少、且查询结果能按其他维度收敛」的场景。

四、如何量化倾斜

-- 在每个物理分片上执行,汇总后比较 max/avg
SELECT 'ds0.t_order_0' AS shard, COUNT(*) AS rows_cnt, MAX(id) AS max_id FROM ds0.t_order_0
UNION ALL
SELECT 'ds0.t_order_1', COUNT(*), MAX(id) FROM ds0.t_order_1
UNION ALL
SELECT 'ds1.t_order_0', COUNT(*), MAX(id) FROM ds1.t_order_0;

线上更实用的是按分片打点:把每个物理表的 QPS、P99、磁盘占用上报监控,用 max/avg 比值告警,超过 2 倍即视为倾斜。

五、常见追问

Q1:加盐后范围查询怎么办? 扇出到所有盐位再归并,或用映射表记录「业务键 → 盐位」。若范围查询是主场景,就不要加盐,改用「范围 + 哈希」的组合分片。

Q2:一致性哈希能解决倾斜吗? 能缓解节点增减时的重分布,但对热点键无效——同一个 key 永远映射到同一个虚拟节点。

Q3:倾斜到什么程度要处理? 分片间数据量差异超过 30%,或单分片 QPS 达到均值 2 倍且已影响 P99,就该处理;否则优先做归档与索引优化。

小结

  • 倾斜的本质是「分片键的分布」与「访问的分布」不匹配。
  • 加盐能拆写热点,代价是读放大;拆维度、独立库更适合长尾大客户。
  • 用分片级监控量化倾斜,别凭感觉扩容。
笔记加载中…