如何保证分库分表后 ID 的全局唯一性?
分库分表后,会产生一个新的问题:
原来数据库自增 ID 不再可靠了。
单库时代:
id AUTO_INCREMENT数据库天然保证:
1
2
3
4
5
...唯一且递增。
但分库后:
db1:
1
2
3
db2:
1
2
3
db3:
1
2
3不同库都会产生相同 ID。
这会导致:
- 订单号冲突
- 用户 ID 冲突
- 数据无法合并
- 路由失效
因此:
分布式系统必须解决「全局唯一 ID」问题。
一、理想的 ID 应该具备什么特征
一个优秀的分布式 ID 应满足:
1. 全局唯一
任何机器生成的 ID 都不能重复。
这是最基本要求。
2. 趋势递增
最好:
1001
1002
1003而不是:
891723
17
9812374递增 ID 对数据库更友好。
3. 高性能
高并发下:
每秒可能生成:
10万
100万
1000万ID
不能成为系统瓶颈。
4. 高可用
发号器挂掉:
整个系统可能无法下单。
因此:
ID 服务必须高可用。
5. 长度适中
ID 太长:
- 存储成本增加
- 索引变大
- 查询效率下降
二、最简单方案:UUID
很多人第一反应:
UUID.randomUUID()例如:
550e8400-e29b-41d4-a716-446655440000优点:
- 绝对唯一
- 本地生成
- 无中心节点
但UUID 并不适合作为数据库主键。
原因:
1. 太长
128 位。
存储成本高。
2. 无序
完全随机。
例如:
1
1000000
3
500000插入数据库时:
B+树频繁分裂。
3. 索引性能差
随机插入导致:
- 页分裂
- 页迁移
- 缓存命中率下降
因此:
UUID 通常不适合作为数据库主键。
三、数据库自增主键
另一种方案:
专门建立一张表。
CREATE TABLE sequence (
id BIGINT AUTO_INCREMENT
)每次获取 ID:
INSERT INTO sequence VALUES(NULL)然后:
SELECT LAST_INSERT_ID()优点:
- 简单
- 保证唯一
- 天然递增
缺点:
发号器成为单点
所有请求都访问一个数据库。
例如:
10万QPS全打到发号器。
数据库很快成为瓶颈。
因此:
无法支撑大型系统。
四、号段模式(Leaf 方案)
核心思想:
不要每次都找数据库要 ID。
而是一次申请一大段。
例如:
数据库记录:
当前最大ID = 10000某个服务申请:
10001 ~ 11000共 1000 个 ID。
之后:
本地直接发号:
10001
10002
10003
...
11000无需访问数据库。
号段用完后:
再申请下一段。
例如:
11001 ~ 12000五、号段模式为什么性能高
因为:
原来:
每生成1个ID
访问1次数据库变成:
每生成1000个ID
访问1次数据库数据库压力下降:
1000倍因此:
系统吞吐量大幅提升。
六、号段模式的问题
虽然性能高,
但存在:
单点问题
数据库仍然存在。
只是压力变小。
号段浪费
例如:
申请:
10001~11000结果服务重启。
只用了:
10001~10500后面 500 个浪费。
ID 不要求连续。
因此浪费通常可以接受。
七、Snowflake(雪花算法)
这是互联网公司最常用方案。
Twitter 提出。
核心思想:
不依赖数据库。
完全本地生成。
64位结构:
1bit 符号位
41bit 时间戳
10bit 机器ID
12bit 序列号生成结果:
时间
+
机器编号
+
自增序号组合起来唯一。
八、Snowflake 为什么能保证唯一
例如:
机器1:
机器ID=1生成:
时间戳 + 1 + 序列号机器2:
机器ID=2生成:
时间戳 + 2 + 序列号机器编号不同。
自然不会冲突。
九、Snowflake 的优势
1. 无中心节点
无需访问数据库。
2. 性能极高
完全内存生成。
单机:
几十万到百万 QPS。
3. 趋势递增
前 41 位是时间戳。
因此:
整体递增。
4. 适合分布式
目前:
- 美团 Leaf
- 百度 UidGenerator
- 滴滴 TinyID
本质都类似。
十、Snowflake 的问题
时钟回拨
例如:
当前时间:
1000生成了一批 ID。
NTP 校时后:
系统时间变成:
900那么:
可能生成重复 ID。
这是 Snowflake 最大风险。
常见解决方案:
- 拒绝发号
- 等待时间恢复
- 备用机器
十一、实际工程选择
文章给出的思路是:
中小系统
可以:
- UUID
- 数据库自增
实现简单。
大型互联网系统
通常:
- 号段模式
- Snowflake
二选一。
如果追求:
简单可靠
选:
号段模式
美团 Leaf Segment。
如果追求:
极致性能
选:
Snowflake
本地生成。
十二、本章核心脉络
系统演进过程:
单库
↓
数据库自增ID
分库分表
↓
ID冲突
需要全局唯一ID
↓
UUID
↓
数据库发号器
↓
号段模式
↓
Snowflake一句话总结
分库分表后,数据库自增 ID 无法保证全局唯一,因此需要独立的分布式发号机制。工程上最常见的两种方案是号段模式和 Snowflake 算法:前者通过批量申请 ID 降低数据库压力,后者通过时间戳、机器号和序列号组合实现本地高性能生成,两者本质上都是为了解决“全局唯一、高性能、高可用”的 ID 生成问题。