先说结论:分片键不是选「均匀」,是选「爆炸半径」
我 2021 年做订单系统,分片键选了 order_id,因为订单号全局唯一,看起来最公平。结果运营后台要按用户手机号查订单列表,查询直接扫 64 个库,p99 从 80ms 干到 1.2s,DBA 在群里骂了我三天。
后来我复盘,问题不在 MySQL,也不在哈希算法,而在我把「均匀」当成了目标。系统设计里,分片键真正决定的是:最坏情况下,一个热点、一次扩容、一次误删,会炸掉多少分片。
如果你只记住一句话:先画查询矩阵,再谈一致性哈希。下面是我现在用的对比表和步骤,数字都是当时线上真实参数,能查的我都写了。
三种主流分片方案,别只看扩容
| 方案 | 扩容成本 | 热点隔离 | 路由成本 | 运维复杂度 | 我见过的适用场景 |
|---|---|---|---|---|---|
哈希取模 hash(key) % N |
改 N 要全量迁移 | 差,热 key 绑死分片 | O(1) | 低 | 数据 500GB 以内、基本不扩容的内部系统 |
| 一致性哈希 + 虚拟节点 | 只迁相邻区间 | 中,热点仍集中 | O(log N) | 中 | Redis 客户端分片、缓存节点扩缩容 |
| 预分片 + 路由表 | 逻辑分片固定,物理库可加 | 好,可手动打散 | O(1) 查表 | 高 | 订单、支付、IM 这种不能停的业务 |
Redis Cluster 是另一条路:它固定 16384 个哈希槽,用 CRC16(key) mod 16384 定位槽,槽再分配给节点。2023 年我们一个评论点赞 key 打爆单分片,单分片 12 万 QPS,CPU 92%,p99 从 35ms 到 180ms。后来把 key 拆成 16 个随机后缀做本地聚合,单分片降到 8000 QPS,代价是读放大 16 倍。
所以一致性哈希不是银弹。它擅长节点增减,不擅长数据模型本身的热点。你按 user_id 分片,大 V 用户就是天然热点;你按 order_id 分片,用户列表查询就是天然跨分片。分片键解决不了所有查询,只能解决主查询。
一个订单系统的容量估算,别拍脑袋
假设日增 3000 万订单,保留 3 年:3000 万 × 365 × 3 = 328.5 亿行。单行加索引按 1KB 算,约 3.28TB,算上索引和副本,主库至少 5TB。
如果单表控制在 1000 万行,需要 3285 张表。我当时选 64 库 × 64 表 = 4096 张,每张约 800 万行,B+ 树通常 3 层,主键查 P99 能压在 35ms 左右。QPS 写 5000,峰值按 3 倍算 15000,平均到 64 库是每库 234 QPS,看起来轻松,但热点库能到 5000 QPS。
分片数最好留 4 到 8 倍余量。你当前 8 库,不要只按 8 库设计,逻辑上先定 64 或 256 个逻辑分片,路由表放配置中心,4096 条路由记录按 16 字节算也就 64KB,内存完全放得下。这样以后加物理库,改的是映射,不是全量 rehash。
迁移参数也要提前算:2TB 历史数据,按 50MB/s 拉,约 11.6 小时;双写期间每天 3000 万单,单行 1KB,日增 30GB,双写放大到 60GB/天。双写窗口我一般留 7 天,灰度 1% → 10% → 50% → 100%,旧库只读保留 14 天,出问题能回滚。
我现在选分片键的 6 个步骤
- 写查询矩阵:把 Top 10 查询列出来,标清楚哪些带分片键、频率、P99 要求。比如「用户查订单列表」占 70%,那就必须让 user_id 进分片键。
- 定保留策略:数据是 180 天归档还是 3 年在线?如果 180 天归档,时间分片 + 哈希二级分片比纯一致性哈希省存储,冷数据还能压缩。
- 估峰值:写 QPS 5000,峰值 15000,热点单 key 可能 1 万 QPS。热 key 阈值别等 CPU 90% 才处理,单分片超过均值 3 倍就告警。
- 选分片键:优先选最高频查询条件。订单详情按 order_id 查,用户列表按 user_id 查,就用基因分片,把 user_id 后 6 位编进 order_id 低位。
- 定逻辑分片数:按 3 年数据量算,再乘 2 到 4 倍。328.5 亿行选 4096 张表,每张 800 万行;如果增长快,直接 8192 张,别等 2000 万行再拆。
- 上线监控:p99、分片 QPS 方差、双写不一致数、迁移延迟。分片 QPS 方差超过 3 倍告警,p99 超过 200ms 告警,双写不一致超过 0.01% 停灰度。
这里有个反常识点:一致性哈希适合「节点会频繁增减、数据没有明显用户维度」的场景,比如 CDN 缓存、Redis 客户端分片。订单、支付、社交关系这种有强用户维度的业务,预分片加路由表更土,但更可控。土不等于差,系统设计里可控比优雅值钱。
最后说点人话
我现在面试别人,听到「上一致性哈希」会先问:你分片键是什么?跨分片查询怎么办?扩容时 p99 多少?迁移窗口多久?如果答不上来,说明还没落地过。
我自己也翻过车。2021 年那次订单查询事故后,我们把 order_id 查询保留在分片内,用户列表走异构索引表,ES 里只存 order_id、user_id、状态、时间四个字段,每天 3000 万条,索引 90GB 左右,查询 P99 120ms。能接受,因为后台不是支付主链路。
如果重来一次,我会先花半天画查询矩阵,再花半天算容量,最后才讨论哈希。分片键选错,后面所有优化都是给错误擦屁股;分片键选对,一致性哈希、预分片、时间分片都只是工具。
(完)