一、分布式全局唯一 ID 生成器
1. 业务背景
秒杀下单会生成优惠券订单,存入tb_voucher_order订单表。 数据库自增 ID 存在缺陷
- ID 规律性太强,容易被爬虫推算订单数量
- 分库分表场景下,单表自增会出现 ID 重复,受单表数据量约束
2. 全局 ID 五大核心特性
- 唯一性:整个分布式系统不能出现重复 ID
- 高性能:高并发秒杀场景,生成 ID 速度要快
- 高可用:不能因为 ID 生成服务故障导致业务瘫痪
- 递增性:ID 大体有序,方便数据库索引
- 安全性:ID 不能轻易被猜测、推算业务数据
3. 常见 ID 生成方案对比
- UUID:无序,字符串,数据库索引性能差
- 数据库自增:简单;分库分表会冲突,ID 有规律不安全
- 雪花算法 snowflake:本地计算生成,性能高;依赖系统时间,时间回拨会产生重复 ID
- Redis 自增:利用 Redis 的 incr 做计数器,性能强;依赖 Redis 服务
4. Redis 自增 ID 方案(黑马点评使用)
- key 按日期划分,每天一个 key,便于统计每日订单数量
- ID 结构:时间戳 + Redis 自增序列号
bit 位结构:
- 符号位:1bit,固定为 0,保证正数
- 时间戳:31bit,单位秒,可以覆盖约 69 年
- 序列号:32bit,秒内计数器,同一秒内,每个请求拿到不同序号,每秒可生成海量 ID
原理:拿到当前时间戳,再用 Redis incr 拿到当天 key 的自增数字,拼接组合得到完整全局 ID。
java
@Component
public class RedisIdWorker {
private static final long BEGIN_TIMESTAMP = 1787673600L;
private StringRedisTemplate stringRedisTemplate;
public long nexId(String keyPrefix) {
//生成时间戳
LocalDateTime now = LocalDateTime.now();
long epochSecond = now.toEpochSecond(ZoneOffset.UTC);
Long timestamp = epochSecond - BEGIN_TIMESTAMP;
//生成序列号
String yyyyMMdd = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
Long increment = stringRedisTemplate.opsForValue().increment("icr" + keyPrefix + ":" + yyyyMMdd);
return timestamp << 32 | increment;
}
public static void main(String[] args) {
LocalDateTime localDateTime = LocalDateTime.of(2026, 8, 25, 0, 0, 0);
long epochSecond = localDateTime.toEpochSecond(ZoneOffset.UTC);
System.out.println("second" + epochSecond);
}
}