在分布式系统中,数据库分库分表后,传统自增 ID(AUTO_INCREMENT)无法保证全局唯一 —— 不同分表可能生成相同 ID,导致数据冲突。分布式 ID 生成技术,就是为跨库、跨表的全局数据提供唯一标识的解决方案。
分布式 ID 的核心要求
一个可靠的分布式 ID 需满足:
- 全局唯一:这是基本要求,绝不能重复
- 趋势递增:便于数据库建立索引(B + 树索引偏好递增数据)
- 高性能:生成速度快,不成为系统瓶颈
- 高可用:生成服务不能单点故障
- 可追溯:最好能包含时间戳等信息,便于问题排查
主流实现方案
1. 雪花算法(Snowflake):性能与灵活性兼顾
这是 Twitter 开源的分布式 ID 生成算法,生成 64 位 Long 型 ID,结构如下:
-
1 位符号位(固定 0,确保正数)
-
41 位时间戳(毫秒级,可使用约 69 年)
-
10 位机器 ID(支持 1024 台机器)
-
12 位序列号(每台机器每秒可生成 4096 个 ID)
优势:
-
纯内存生成,性能极高(单机每秒可达百万级)
-
包含时间戳,支持按时间排序
-
可自定义机器 ID 位数,适应不同集群规模
代码简化示例:
public class SnowflakeIdGenerator {
private final long workerId; // 机器ID
private final long datacenterId; // 数据中心ID
private long sequence = 0L; // 序列号
private long lastTimestamp = -1L; // 上次生成ID的时间戳
public SnowflakeIdGenerator(long workerId, long datacenterId) {
// 校验workerId和datacenterId范围
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 处理时钟回拨(关键逻辑)
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
// 同一毫秒内,序列号递增
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & 0xFFF; // 12位序列号掩码
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp); // 等待下一毫秒
}
} else {
sequence = 0L; // 不同毫秒,序列号重置
}
lastTimestamp = timestamp;
// 组合ID:时间戳 << 22 | 数据中心ID << 17 | 机器ID << 12 | 序列号
return (timestamp - 1288834974657L) << 22
| (datacenterId << 17)
| (workerId << 12)
| sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
注意事项:
- 时钟回拨问题:服务器时钟若倒退,可能生成重复 ID,需通过算法容错(如等待时钟追上)或引入闰秒处理
- 机器 ID 分配:需确保集群中机器 ID 唯一,可通过配置中心或启动时争抢分布式锁获取
2. 数据库号段模式:简单可靠但有性能瓶颈
原理:从数据库预分配一段 ID(如 1-1000),本地缓存使用,用完再申请下一段。
- 优势:实现简单,ID 绝对递增
- 劣势:数据库可能成为瓶颈,需做好主从备份
3. Redis 自增:分布式环境下的 “计数器”
利用 Redis 的INCR命令生成自增 ID,结合 Lua 脚本确保原子性:
// 生成ID示例(RedisTemplate)
Long id = redisTemplate.opsForValue().increment("distributed:id:order", 1);
- 优势:实现简单,性能高于数据库
- 劣势:Redis 若宕机可能丢失 ID 生成状态,需持久化配置(AOF+RDB)
方案选择建议
-
高并发场景(如电商订单):优先选雪花算法,兼顾性能与 ID 有序性
-
中小规模系统:Redis 自增或数据库号段模式更易实现
-
需兼容旧系统:可考虑 UUID(但无序,不适合做数据库主键)或基于业务字段拼接(如用户 ID + 时间戳 + 随机数)
分布式 ID 生成看似简单,实则关乎系统稳定性 —— 一个重复 ID 可能导致订单混乱、支付异常等严重问题,需结合业务规模和性能需求,选择最适合的方案。