本文目录导读:

我将为您提供几个Java实现分布式ID的完整案例,涵盖不同方案。
雪花算法(Snowflake)实现
基础版本
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicLong;
public class SnowflakeIdGenerator {
// 起始时间戳 (2020-01-01)
private static final long START_TIME = 1577808000000L;
// 各部分占用的位数
private static final long SEQUENCE_BITS = 12; // 序列号占12位
private static final long MACHINE_ID_BITS = 5; // 机器ID占5位
private static final long DATACENTER_ID_BITS = 5; // 数据中心ID占5位
// 各部分最大值
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
// 各部分向左的位移
private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATACENTER_ID_BITS;
private final long datacenterId; // 数据中心ID
private final long machineId; // 机器ID
private long sequence = 0L; // 序列号
private long lastTimestamp = -1L; // 上次生成ID的时间戳
public SnowflakeIdGenerator(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId must be between 0 and " + MAX_DATACENTER_ID);
}
if (machineId > MAX_MACHINE_ID || machineId < 0) {
throw new IllegalArgumentException("machineId must be between 0 and " + MAX_MACHINE_ID);
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
public synchronized long nextId() {
long currentTimestamp = System.currentTimeMillis();
if (currentTimestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {
// 序列号用尽,等待下一毫秒
currentTimestamp = waitNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = currentTimestamp;
return ((currentTimestamp - START_TIME) << TIMESTAMP_SHIFT)
| (datacenterId << DATACENTER_ID_SHIFT)
| (machineId << MACHINE_ID_SHIFT)
| sequence;
}
private long waitNextMillis(long lastTimestamp) {
long currentTimestamp = System.currentTimeMillis();
while (currentTimestamp <= lastTimestamp) {
currentTimestamp = System.currentTimeMillis();
}
return currentTimestamp;
}
// 获取本机IP地址生成workerId
public static long getWorkerId() {
try {
InetAddress ip = InetAddress.getLocalHost();
byte[] ipAddress = ip.getAddress();
return ((ipAddress[ipAddress.length - 1] & 0xFF) % 32);
} catch (UnknownHostException e) {
return Math.abs(java.util.UUID.randomUUID().hashCode()) % 32;
}
}
}
使用示例
public class SnowflakeExample {
public static void main(String[] args) {
// 创建生成器
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
// 生成单个ID
long id = generator.nextId();
System.out.println("生成的ID: " + id);
// 批量生成
for (int i = 0; i < 10; i++) {
System.out.println("ID " + (i+1) + ": " + generator.nextId());
}
// 并发测试
ExecutorService executor = Executors.newFixedThreadPool(10);
Set<Long> ids = ConcurrentHashMap.newKeySet();
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
long newId = generator.nextId();
ids.add(newId);
});
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生成的唯一ID数量: " + ids.size());
}
}
基于数据库的ID生成方案
数据库表结构
CREATE TABLE `distributed_id` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `biz_type` varchar(50) NOT NULL COMMENT '业务类型', PRIMARY KEY (`id`), UNIQUE KEY `uk_biz_type` (`biz_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Java实现
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DatabaseIdGenerator {
private final DataSource dataSource;
private final int step; // 步长
public DatabaseIdGenerator(DataSource dataSource, int step) {
this.dataSource = dataSource;
this.step = step;
}
public long generateId(String bizType) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
// 更新并获取新的ID范围
String sql = "UPDATE distributed_id SET id = LAST_INSERT_ID(id + ?) WHERE biz_type = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, step);
pstmt.setString(2, bizType);
pstmt.executeUpdate();
// 获取当前ID
sql = "SELECT LAST_INSERT_ID()";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
return rs.getLong(1) - step + 1;
}
return -1;
} catch (SQLException e) {
throw new RuntimeException("Failed to generate ID", e);
} finally {
// 关闭资源
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
批量获取IDRange
public class IdRange {
private final long minId;
private final long maxId;
private long currentId;
public IdRange(long minId, long maxId) {
this.minId = minId;
this.maxId = maxId;
this.currentId = minId;
}
public synchronized long nextId() {
if (currentId > maxId) {
return -1; // 范围已用尽
}
return currentId++;
}
public boolean isExhausted() {
return currentId > maxId;
}
public long getMinId() { return minId; }
public long getMaxId() { return maxId; }
public long getCurrentId() { return currentId; }
}
Redis原子操作方案
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;
public class RedisIdGenerator {
private final JedisPool jedisPool;
private final String keyPrefix;
private final int step = 100; // 步长
public RedisIdGenerator(JedisPool jedisPool, String keyPrefix) {
this.jedisPool = jedisPool;
this.keyPrefix = keyPrefix;
}
public long generateId(String bizType) {
String key = keyPrefix + ":" + bizType;
try (Jedis jedis = jedisPool.getResource()) {
// 使用INCR命令原子递增
return jedis.incr(key);
}
}
// 批量获取ID(每批次减少网络开销)
public IdRange generateIdRange(String bizType) {
String key = keyPrefix + ":" + bizType;
try (Jedis jedis = jedisPool.getResource()) {
long minId = jedis.incrBy(key, step) - step + 1;
long maxId = minId + step - 1;
// 初始化缓存中的id范围
return new IdRange(minId, maxId);
}
}
// 使用Lua脚本保证原子性
public long generateIdWithLua(String bizType, int count) {
String key = keyPrefix + ":" + bizType;
String luaScript =
"local current = redis.call('GET', KEYS[1]) " +
"if not current then current = 0 end " +
"local next = tonumber(current) + tonumber(ARGV[1]) " +
"redis.call('SET', KEYS[1], next) " +
"return next";
try (Jedis jedis = jedisPool.getResource()) {
Object result = jedis.eval(luaScript, 1, key, String.valueOf(count));
return Long.parseLong(result.toString());
}
}
}
UUID方案(简单但有缺点)
import java.util.UUID;
public class UUIDGenerator {
// 标准UUID
public String generateUUID() {
return UUID.randomUUID().toString();
}
// 去除连字符的UUID
public String generateUUIDWithoutDash() {
return UUID.randomUUID().toString().replace("-", "");
}
// 数字格式的UUID(适合作为Long类型ID)
public Long generateNumericId() {
return Math.abs(UUID.randomUUID().getMostSignificantBits());
}
// 转化为Base64缩短长度
public String generateShortUUID() {
UUID uuid = UUID.randomUUID();
byte[] bytes = new byte[16];
long most = uuid.getMostSignificantBits();
long least = uuid.getLeastSignificantBits();
for (int i = 0; i < 8; i++) {
bytes[i] = (byte) (most >>> (56 - i * 8));
bytes[8 + i] = (byte) (least >>> (56 - i * 8));
}
return java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}
完整生产级方案(组合实现)
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
public class DistributedIdGenerator {
// 配置
private static class IdConfig {
final long datacenterId;
final long workerId;
final long timestampBits = 41;
final long datacenterBits = 5;
final long workerBits = 5;
final long sequenceBits = 12;
final long maxSequence = ~(-1L << sequenceBits);
final long maxWorker = ~(-1L << workerBits);
final long maxDatacenter = ~(-1L << datacenterBits);
final long workerShift = sequenceBits;
final long datacenterShift = sequenceBits + workerBits;
final long timestampShift = sequenceBits + workerBits + datacenterBits;
long startTimestamp = 1577808000000L; // 2020-01-01
final long maxTimestamp = ~(-1L << timestampBits) + startTimestamp;
IdConfig(long datacenterId, long workerId) {
if (datacenterId > maxDatacenter || datacenterId < 0) {
throw new IllegalArgumentException("Invalid datacenterId");
}
if (workerId > maxWorker || workerId < 0) {
throw new IllegalArgumentException("Invalid workerId");
}
this.datacenterId = datacenterId;
this.workerId = workerId;
}
}
// 各种生成器实例
private final SnowflakeIdGenerator snowflakeGenerator;
private final RedisIdGenerator redisGenerator;
private final DatabaseIdGenerator databaseGenerator;
private final UUIDGenerator uuidGenerator;
// 缓存用于当前ID生成策略
private final ConcurrentHashMap<String, AtomicLong> idCache = new ConcurrentHashMap<>();
public DistributedIdGenerator() {
// 初始化各生成器
this.snowflakeGenerator = new SnowflakeIdGenerator(1, 1);
this.redisGenerator = null; // 需要配置Redis连接池
this.databaseGenerator = null; // 需要配置数据源
this.uuidGenerator = new UUIDGenerator();
}
// 根据不同的业务场景选择合适的ID生成策略
public long generateSnowflakeId() {
return snowflakeGenerator.nextId();
}
public String generateUUID() {
return uuidGenerator.generateUUIDWithoutDash();
}
// 带缓存策略的ID生成
public long generateCachedId(String bizType) {
AtomicLong cache = idCache.computeIfAbsent(bizType, k -> new AtomicLong(0));
long id = cache.getAndIncrement();
// 如果缓存中的ID不够用,从数据库获取新的范围
if (id >= 1000000) { // 假设每100万需要刷新
// 从数据库获取新的ID范围
// idCache.put(bizType, new AtomicLong(newRangeStart));
}
return id;
}
}
使用示例
public class DistributedIdDemo {
public static void main(String[] args) {
// 1. 雪花算法
SnowflakeIdGenerator snowflake = new SnowflakeIdGenerator(1, 1);
System.out.println("雪花算法 ID: " + snowflake.nextId());
// 2. UUID
UUIDGenerator uuidGen = new UUIDGenerator();
System.out.println("UUID: " + uuidGen.generateUUIDWithoutDash());
// 3. 并发测试
ConcurrentHashMap<Long, Boolean> idSet = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(20);
long startTime = System.currentTimeMillis();
// 提交10000个任务
CountDownLatch latch = new CountDownLatch(10000);
for (int i = 0; i < 10000; i++) {
executor.submit(() -> {
long id = snowflake.nextId();
idSet.put(id, true);
latch.countDown();
});
}
try {
latch.await(10, TimeUnit.SECONDS);
long endTime = System.currentTimeMillis();
System.out.println("生成10000个唯一ID数量: " + idSet.size());
System.out.println("耗时: " + (endTime - startTime) + "ms");
System.out.println("平均耗时: " + (startTime - endTime) / 10000.0 + "ms/个");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
最佳实践建议
public class IdGeneratorFactory {
// 根据业务需求选择合适的ID生成策略
public static IdGeneratorStrategy createStrategy(IdType type, IdConfig config) {
switch (type) {
case SNOWFLAKE:
return new SnowflakeStrategy(config);
case REDIS:
return new RedisStrategy(config);
case DATABASE:
return new DatabaseStrategy(config);
case MIXED:
return new MixedStrategy(config);
default:
throw new IllegalArgumentException("Unsupported ID type");
}
}
enum IdType {
SNOWFLAKE, // 雪花算法
REDIS, // Redis生成
DATABASE, // 数据库生成
MIXED // 混合策略
}
interface IdGeneratorStrategy {
long nextId(String bizType);
String nextIdString(String bizType);
}
static class SnowflakeStrategy implements IdGeneratorStrategy {
private final SnowflakeIdGenerator generator;
SnowflakeStrategy(IdConfig config) {
this.generator = new SnowflakeIdGenerator(config.datacenterId, config.workerId);
}
@Override
public long nextId(String bizType) {
return generator.nextId();
}
@Override
public String nextIdString(String bizType) {
return String.valueOf(generator.nextId());
}
}
// 其他策略实现...
}
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 雪花算法 | 高性能、趋势递增、无网络依赖 | 依赖机器时钟 | 高并发分布式环境 |
| 数据库 | 简单、可靠 | 性能瓶颈、需维护 | 小规模系统 |
| Redis | 高性能、灵活 | 依赖Redis、持久化问题 | 中高并发系统 |
| UUID | 简单、全局唯一 | 无序、存储浪费 | 非索引字段 |
选择合适的方案需要根据业务场景、并发量、系统架构等因素综合考虑,建议在实际生产环境中结合多种方案使用。