最近在深入学习 Java 后端和 Redis 中间件时,遇到了一个非常经典且重要的问题:在分布式场景下,如何生成一个全局唯一的 ID?
在单体架构时代,我们习惯使用数据库的自增 ID(Auto Increment),但在分库分表、微服务的高并发场景下,这种方式由于性能瓶颈和单点问题,显然已经力不从心。
今天这篇博客就来总结一下目前业界最主流的 4 种全局唯一 ID 生成策略,分析它们的原理、优缺点以及适用场景。
在设计 ID 生成器之前,我们需要明确“好 ID”的标准。通常有以下几个核心要求:

UUID 是最简单、最暴力的方案。JDK 原生支持,一行代码搞定。
public static void main(String[] args) {
// 生成一个 UUID,并去掉中间的横线
String id = UUID.randomUUID().toString().replace("-", "");
System.out.println("UUID: " + id);
}👉 结论:适合生成 Token、Session ID 或非数据库主键的场景。坚决不建议用作 MySQL 的主键。

利用 MySQL 的 auto_increment 特性,或者 Oracle 的 Sequence。
应用服务向数据库插入数据,数据库自动累计 ID。
👉 结论:适合并发量不高的中小项目,或者不需要分库分表的数据表。

Redis 是单线程处理命令的,其 INCR 命令是原子的,天生适合做计数器。这是我最近在学 Redis 时觉得非常有意思的一个应用点。
为了避免 ID 被推测出业务量,通常会结合“时间戳”使用。
格式示例:yyyyMMdd + Redis自增值。
// 伪代码示例
public long generateId(String keyPrefix) {
// 1. 生成时间戳部分
String dateStr = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now());
// 2. 利用 Redis 原子递增
// key 举例: icr:order:20251216
Long increment = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + dateStr);
// 3. 拼接 ID (实际生产中通常需要通过位运算或字符串填充补齐位数)
return Long.parseLong(dateStr + String.format("%06d", increment));
}👉 结论:非常适合高并发的业务场景(如秒杀、订单生成),且生成的 ID 具有业务含义。

这是目前分布式系统中最流行、最成熟的方案,由 Twitter 开源。它的核心思想是将一个 64 位的 long 型数字切割成不同的部分。
通常不需要自己手写位运算,推荐使用成熟的工具包,例如 Hutool。
// 引入 Hutool 依赖后
public class IdTest {
public static void main(String[] args) {
// 参数1: 终端ID, 参数2: 数据中心ID
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
long id = snowflake.nextId();
System.out.println("Snowflake ID: " + id);
}
}👉 结论:几乎所有互联网大厂的主流选择,适合超大规模的分布式系统。
最后,用一张表来总结这几种策略:
策略 | 唯一性 | 有序性 | 性能 | 依赖组件 | 核心痛点 |
|---|---|---|---|---|---|
UUID | 高 | 无 | 极高 | 无 | 索引性能差,ID太长 |
DB自增 | 高 | 严格有序 | 低 | 数据库 | 并发瓶颈,扩展麻烦 |
Redis | 高 | 严格有序 | 高 | Redis | 依赖 Redis 高可用 |
Snowflake | 高 | 趋势有序 | 极高 | 无 | 时钟回拨问题 |
个人建议:
如果你是初学者或者项目规模较小,Redis 自增是一个非常好的练手方案,既能满足性能要求,又能加深对 Redis 的理解。而如果是企业级的大型项目,Snowflake(配合 Hutool 等工具库)则是目前的最优解。
希望这篇总结对大家有所帮助!如果你有更好的方案,欢迎在评论区交流。
本文由一名热爱技术的研二学生整理,持续分享 Java 后端与算法学习心得。