P31 分库分表 id 冲突解决方案
面试题:分库分表后,多个库/表各自生成主键,怎么避免 ID 冲突?
核心问题
单库自增主键在分库分表后失效:两个库都从 1 自增,会生成重复 ID。
常见方案对比
1. 数据库分段/步长自增
text
库1:起始 1,步长 4:1, 5, 9, 13...
库2:起始 2,步长 4:2, 6, 10, 14...配置 auto_increment_offset + auto_increment_increment。
- 优点:简单、无额外组件;
- 缺点:扩容要改步长(老数据 ID 段与新步长冲突),适合表数量固定的场景。
2. 号段模式(推荐)
用一个发号表批量取号段:
sql
UPDATE id_generator SET max_id = max_id + 1000 WHERE biz_type = 'order';
SELECT max_id - 1000, max_id FROM id_generator WHERE biz_type = 'order';服务拿到 [10001, 11000] 号段在本地内存分配,用完再取下一段。
- 优点:性能高(一次取号服务 N 次用)、实现简单;
- 缺点:依赖 DB,号段用完前要提前预取。
3. Redis 原子自增
INCR id:order 获取全局唯一 ID。
- 优点:快、简单;
- 缺点:依赖 Redis 可用性,纯自增可预测(不适合订单号暴露场景)。
4. UUID
UUID.randomUUID()。
- 优点:本地生成、无依赖;
- 缺点:无序(作主键会导致 B+ 树页分裂、性能差)、太长(36 字符)。
5. Snowflake(雪花算法)— 生产最常用
text
64 位 = 1位符号位 + 41位时间戳 + 10位机器ID + 12位序列号- 趋势递增、分布式无中心、每秒单机约 409.6 万个 ID;
- 注意点:时钟回拨要处理(等待/拒绝)、机器 ID 唯一性、序列号溢出;
- 变体:美团 Leaf(号段 + 雪花)、百度 UidGenerator。
6. 时间戳 + 随机/业务组合
如 yyyyMMddHHmmss + 随机数,简单但不保证绝对唯一,要配唯一索引兜底。
方案选型建议
| 场景 | 推荐 |
|---|---|
| 订单号等需趋势递增、高性能 | Snowflake / 号段 |
| 简单业务、表固定 | 分段自增 |
| 无中心化诉求、容忍无序 | UUID(避免做主键) |
| 高频短号(验证码/短链) | Redis INCR |
一句话总结
分库分表后 ID 冲突用"号段模式 / Snowflake 雪花算法"解决最主流:号段靠 DB 发号表批量取、雪花靠时间戳+机器ID+序列号本地生成;UUID 无序、Redis 有依赖,按场景选择。