推荐答案
在 Java 中实现一个分布式 ID 生成器,可以使用以下几种常见的方法:
1. 使用 Snowflake 算法
Snowflake 是 Twitter 开源的一种分布式 ID 生成算法,生成的 ID 是一个 64 位的长整型数字,结构如下:
- 1 位符号位(始终为 0)
- 41 位时间戳(毫秒级)
- 10 位机器 ID(可以配置)
- 12 位序列号(每毫秒生成 4096 个 ID)
// www.javascriptcn.com code example
public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L; // 起始时间戳
private final long workerIdBits = 10L; // 机器 ID 位数
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大机器 ID
private final long sequenceBits = 12L; // 序列号位数
private final long workerIdShift = sequenceBits; // 机器 ID 左移位数
private final long timestampLeftShift = sequenceBits + workerIdBits; // 时间戳左移位数
private final long sequenceMask = -1L ^ (-1L << sequenceBits); // 序列号掩码
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id can't be greater than " + maxWorkerId + " or less than 0");
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(workerId << workerIdShift) |
sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}2. 使用 UUID
UUID 是一种通用唯一标识符,可以生成一个 128 位的唯一 ID。Java 中可以使用 java.util.UUID 类来生成 UUID。
import java.util.UUID;
public class UUIDGenerator {
public static String generateUUID() {
return UUID.randomUUID().toString();
}
}3. 使用数据库自增 ID
可以通过数据库的自增 ID 功能来生成分布式 ID。每个应用实例在插入数据时,数据库会自动生成一个唯一的 ID。
// www.javascriptcn.com code example
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class DatabaseIdGenerator {
private static final String DB_URL = "jdbc:mysql://localhost:3306/test";
private static final String USER = "root";
private static final String PASS = "password";
public long generateId() {
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
String sql = "INSERT INTO id_generator (stub) VALUES ('a')";
PreparedStatement pstmt = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
pstmt.executeUpdate();
ResultSet rs = pstmt.getGeneratedKeys();
if (rs.next()) {
return rs.getLong(1);
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
}本题详细解读
1. Snowflake 算法
Snowflake 算法是一种基于时间戳的分布式 ID 生成算法,适用于高并发场景。它的优点是生成的 ID 是趋势递增的,且不依赖于数据库等外部系统。缺点是如果系统时钟回拨,可能会导致 ID 重复。
2. UUID
UUID 是一种通用唯一标识符,生成的 ID 是全局唯一的。优点是生成简单,不依赖于任何外部系统。缺点是生成的 ID 较长,且无序,不适合作为数据库主键。
3. 数据库自增 ID
通过数据库的自增 ID 功能生成分布式 ID,优点是实现简单,且生成的 ID 是有序的。缺点是依赖于数据库,可能会成为系统的瓶颈。
在实际应用中,可以根据具体需求选择合适的分布式 ID 生成方案。