SnowFlake
SnowFlake
是一个生成全局唯一 ID 的算法,它的核心思想是使用一个 64 位的整数来表示一个 ID,其中包含了时间戳、机器 ID、数据中心 ID 和序列号等信息。
SnowFlake
的优点是生成的 ID 是全局唯一的,并且是有序的,这对于一些需要生成唯一 ID 的场景非常有用,比如分布式系统中的主键生成、分布式事务中的唯一标识等。
SnowFlake
的缺点是依赖于时钟,如果时钟回拨,会导致生成的 ID 重复。
当前框架用于生成ID, 就是用的这个工具。
实现如下:
java
package com.neegix.utils;
/**
* 分布式ID生成器(雪花算法)
* 支持两种使用方式:
* 1. 静态方法直接调用:SnowFlake.nextId()
* 2. 实例化后调用:new SnowFlake(datacenterId, workerId).nextId()
*/
public class SnowFlake {
// 静态单例实例(默认datacenterId=1, workerId=1)
private static final SnowFlake DEFAULT_INSTANCE = new SnowFlake(1, 1);
// 常量定义
private static final long DATACENTER_ID_BITS = 2L;
private static final long WORKER_ID_BITS = 2L;
private static final long SEQUENCE_BITS = 10L;
// 位移计算
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
// 起始时间戳(2021-01-01 08:00:00)
private static final long TWEPOCH = 1609459200000L;
// 序列号掩码
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
// 实例变量
private final long datacenterId;
private final long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
/**
* 构造函数
* @param datacenterId 数据中心ID (0-3)
* @param workerId 工作节点ID (0-3)
*/
public SnowFlake(long datacenterId, long workerId) {
long maxDatacenterId = ~(-1L << DATACENTER_ID_BITS);
long maxWorkerId = ~(-1L << WORKER_ID_BITS);
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("Datacenter ID must be between 0 and %d", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("Worker ID must be between 0 and %d", maxWorkerId));
}
this.datacenterId = datacenterId;
this.workerId = workerId;
}
/**
* 静态方法:直接获取ID(使用默认实例)
*/
public static long generateId() {
return DEFAULT_INSTANCE.nextInstanceId();
}
/**
* 实例方法:获取下一个ID
*/
public synchronized long nextId() {
return nextInstanceId();
}
/**
* 实际的ID生成逻辑
*/
private synchronized long nextInstanceId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
| (datacenterId << DATACENTER_ID_SHIFT)
| (workerId << WORKER_ID_SHIFT)
| sequence;
}
/**
* 等待下一个毫秒时间戳
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 获取当前时间戳
*/
private long timeGen() {
return System.currentTimeMillis();
}
}
使用示例:
java
// 静态方法调用
long staticId = SnowFlake.generateId();
// 实例方法调用
SnowFlake snowFlake = new SnowFlake(2, 3); // 假设数据中心ID为2,工作节点ID为3
long instanceId = snowFlake.nextId();