什么是数据倾斜,会带来哪些问题?如何解决?
结论:数据倾斜(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、时间戳拼接的键低位高度相关,容易聚堆。 - 事件驱动:大促、定时任务在同一时刻写同一批键。
三、治理手段
- 加盐(Salting):给热点键拼随机后缀,把一个逻辑分片拆成 N 份。
- 拆分维度:主表按
user_id分片,大商户订单路由到独立库。 - 基因法(Gene Method):把用户 ID 的尾部比特嵌入订单号,使订单号自带路由信息。
- 二级索引表:按
merchant_id建映射表,先查映射再定位物理表。 - 热点隔离:大客户独享实例,或走独立缓存加限流。
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,就该处理;否则优先做归档与索引优化。
小结
- 倾斜的本质是「分片键的分布」与「访问的分布」不匹配。
- 加盐能拆写热点,代价是读放大;拆维度、独立库更适合长尾大客户。
- 用分片级监控量化倾斜,别凭感觉扩容。