1、简述
在 Java 中生成唯一标识符(UUID)是开发过程中常见的需求,特别是在分布式系统或需要确保数据唯一性的场景中。Java 提供了多种方法生成唯一标识符,常用的方式包括使用 UUID 类、Snowflake 算法以及自定义生成逻辑。下面我将介绍这些方法,并为每种方法提供代码示例。

2、使用 UUID 类生成唯一标识符
Java 标准库中的 UUID 类可以轻松生成唯一标识符。UUID 是基于随机数的通用唯一识别码(Universally Unique Identifier),它生成的是 128 位长的数字,通常以 36 个字符(包括 4 个连字符)表示。
import java.util.UUID;public class UUIDExample {public static void main(String[] args) {// 生成一个随机 UUIDUUID uuid = UUID.randomUUID();System.out.println("生成的 UUID: " + uuid.toString());}}
特点:
优点:简单易用,生成速度快。
缺点:不能保证 UUID 的有序性,在高并发场景下可能导致性能问题。
3、基于 Snowflake 算法生成唯一标识符
Snowflake 是 Twitter 开源的分布式唯一 ID 生成算法,生成的 ID 是 64 位的数字。它的特点是高效且有序,特别适用于分布式系统中需要生成全局唯一且时间有序的 ID。
public class SnowflakeIdGenerator {private final long workerId;private final long datacenterId;private final long sequence = 0L;private final long twepoch = 1288834974657L;private final long workerIdBits = 5L;private final long datacenterIdBits = 5L;private final long maxWorkerId = -1L ^ (-1L << workerIdBits);private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);private final long sequenceBits = 12L;private final long workerIdShift = sequenceBits;private final long datacenterIdShift = sequenceBits + workerIdBits;private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final long sequenceMask = -1L ^ (-1L << sequenceBits);private long lastTimestamp = -1L;private long sequenceNum = 0L;public SnowflakeIdGenerator(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException("Worker ID out of range");}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException("Datacenter ID out of range");}this.workerId = workerId;this.datacenterId = datacenterId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards");}if (lastTimestamp == timestamp) {sequenceNum = (sequenceNum + 1) & sequenceMask;if (sequenceNum == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequenceNum = 0L;}lastTimestamp = timestamp;return ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift)| sequenceNum;}private long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}public static void main(String[] args) {SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);for (int i = 0; i < 10; i++) {System.out.println(idGenerator.nextId());}}}
特点:
优点:生成的 ID 有序性好,适合分布式系统。
缺点:实现稍复杂,需要考虑时钟回拨等问题。
4、自定义时间戳加随机数生成唯一标识符
如果项目中对唯一标识的规则有特别的需求,可以根据业务逻辑来自定义生成策略。例如,使用当前时间戳和随机数的组合来生成唯一标识符。
import java.util.Random;public class CustomIdGenerator {public static String generateId() {long timestamp = System.currentTimeMillis();int randomNum = new Random().nextInt(10000);return timestamp + String.format("%04d", randomNum);}public static void main(String[] args) {for (int i = 0; i < 5; i++) {System.out.println("生成的自定义 ID: " + generateId());}}}
特点:
优点:可以根据具体业务需求进行定制。
缺点:需要自己设计生成逻辑和保证唯一性。
5、基于数据库的分布式唯一 ID 生成
通过数据库的自增字段来生成唯一标识符是最简单的一种方式,适用于小规模应用场景。可以利用 MySQL、PostgreSQL 等数据库的自增 ID 功能,生成唯一标识符。
CREATE TABLE orders (id BIGINT AUTO_INCREMENT PRIMARY KEY,order_name VARCHAR(255) NOT NULL);
在插入数据时,数据库会自动生成唯一的自增 ID。
import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;public class DatabaseIdGenerator {public static void main(String[] args) throws Exception {Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");PreparedStatement stmt = conn.prepareStatement("INSERT INTO orders (order_name) VALUES (?)", PreparedStatement.RETURN_GENERATED_KEYS);stmt.setString(1, "order_1");stmt.executeUpdate();ResultSet rs = stmt.getGeneratedKeys();if (rs.next()) {long generatedId = rs.getLong(1);System.out.println("生成的数据库自增 ID: " + generatedId);}conn.close();}}
特点:
优点:实现简单,适合小规模应用。
缺点:扩展性差,数据库压力较大,不适合高并发环境。
6、基于 Redis 的分布式唯一 ID 生成
通过 Redis 自增功能生成唯一标识符也是一种常见的方法,特别适用于高并发场景。Redis 的自增操作是原子性的,因此非常适合分布式环境中生成全局唯一 ID。
import redis.clients.jedis.Jedis;public class RedisIdGenerator {private static Jedis jedis = new Jedis("localhost");public static String generateId(String key) {long id = jedis.incr(key); // 使用 Redis 自增生成 IDreturn String.valueOf(id);}public static void main(String[] args) {for (int i = 0; i < 5; i++) {System.out.println("生成的 Redis ID: " + generateId("order_id"));}}}
特点:
优点:Redis 的自增操作效率高,适合高并发场景。
缺点:需要维护 Redis 集群,并且 Redis 宕机时需要考虑备份机制。
7、美团的 Leaf 分布式 ID 生成器
美团的 Leaf 是一个高可用、低延迟的分布式 ID 生成系统,支持两种模式:号段模式(Segment Model) 和 雪花算法模式(Snowflake Model)。Leaf 通过号段模式为每个业务预分配一段 ID,提高生成效率。
号段模式工作原理:
Leaf 将一段连续的 ID 预分配给应用,避免每次生成 ID 都与数据库交互,减小数据库的压力。
每次只需要更新号段的最大值,可以实现高并发的 ID 生成。
引入 Leaf 的示例:
首先,需要在项目中集成 Leaf,通常通过引入 Leaf 的服务。Leaf 由两部分组成:Leaf Server 和 Leaf Core,Server 是提供 ID 生成服务的接口。假设使用 Maven 构建项目,首先需要引入 Leaf 的依赖:
<dependency><groupId>com.sankuai.inf.leaf</groupId><artifactId>leaf-core</artifactId><version>1.0.2</version></dependency>
号段模式生成 ID:
import com.sankuai.inf.leaf.common.Result;import com.sankuai.inf.leaf.service.SegmentService;import com.sankuai.inf.leaf.service.impl.SegmentServiceImpl;public class LeafSegmentExample {public static void main(String[] args) {// 初始化 SegmentService 实例,通常通过 Spring 容器管理SegmentService segmentService = new SegmentServiceImpl();// 生成唯一标识符Result result = segmentService.getId("order_id");if (result.getStatus() == Result.Status.SUCCESS) {System.out.println("生成的 ID: " + result.getId());} else {System.err.println("ID 生成失败");}}}
特点:
优点:支持高并发,减少数据库操作,号段提前分配。
缺点:需要部署 Leaf 服务,维护相对复杂。
8、 百度的 UIDGenerator 分布式 ID 生成器
百度的 UIDGenerator 是基于雪花算法(Snowflake)的分布式 ID 生成方案,主要优化了时钟回拨的问题,并且支持配置不同的时间位和节点位,满足不同的业务场景。
UIDGenerator 工作原理:
UID 由时间戳、工作机器号、序列号等部分组成。
它通过多种手段减少时钟回拨带来的问题,且生成的 ID 是递增的。
引入 UIDGenerator 的示例:
首先,引入 UIDGenerator 的依赖:
<dependency><groupId>com.baidu.fsg</groupId><artifactId>uid-generator</artifactId><version>1.0.0</version></dependency>
UIDGenerator 示例代码:
import com.baidu.fsg.uid.impl.CachedUidGenerator;import org.springframework.beans.factory.annotation.Autowired;public class UIDGeneratorExample {@Autowiredprivate CachedUidGenerator uidGenerator;public void generateUID() {long uid = uidGenerator.getUID();System.out.println("生成的 UID: " + uid);}}
特点:
优点:高性能,优化了时钟回拨问题,ID 生成速度快。
缺点:依赖性强,需要定制化配置,适合大规模分布式场景。
9、总结
在 Java 中生成唯一标识符有多种方式可选,开发者可以根据项目的需求选择合适的生成方法:
如果只是简单地需要唯一标识,UUID 是最快捷的选择。
在分布式系统中需要有序性且高性能的 ID 生成时,Snowflake 是推荐的选择。
如果有定制化需求,可以通过组合时间戳、随机数等生成自定义唯一标识符。
这篇博客可以结合项目场景讨论各个方法的优缺点,并附带相应的代码示例和输出。




