雪花算法(Snowflake ID)是 Twitter 开源的一种分布式 ID 生成算法,其目的是生成全局唯一的 ID。该算法的核心思想是将一个 64 位的二进制数字分成几个部分,每个部分表示不同的信息,例如数据中心ID、机器ID、序列号等。这些部分的取值范围可以根据实际情况进行调整。
使用雪花算法生成的 ID 具有以下特点:
- 全局唯一,ID 不会重复。
- 按时间有序,新生成的 ID 比旧的 ID 大。
- 可以在分布式环境下生成,不需要中心节点协调。
- 高性能,生成 ID 的速度快。
因其具有全局唯一和分布式特性,常被用于互联网应用的分布式系统中,如订单号生成、数据库主键生成等。
具体实现代码如下:
public class Snowflake {
/** 开始时间戳 (2021-01-01) */
private final long START_TIMESTAMP = 1609430400000L;
/** 机器ID所占的位数 */
private final long WORKER_ID_BITS = 5L;
/** 数据标识ID所占的位数 */
private final long DATA_CENTER_ID_BITS = 5L;
/** 支持的最大机器ID,结果是31 (0B11111) */
private final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
/** 支持的最大数据标识ID,结果是31 (0B11111) */
private final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
/** 序列在ID中占的位数 */
private final long SEQUENCE_BITS = 12L;
/** 机器ID向左移12位 */
private final long WORKER_ID_SHIFT = SEQUENCE_BITS;
/** 数据标识ID向左移17位(12+5) */
private final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
/** 时间戳向左移22位(5+5+12) */
private final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
/** 支持的最大序列号,结果是4095 (0B111111111111) */
private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
/** 工作机器ID */
private final long workerId;
/** 数据中心ID */
private final long dataCenterId;
/** 毫秒内序列号 */
private long sequence = 0L;
/** 上次生成ID的时间戳 */
private long lastTimestamp = -1L;
/**
* 构造函数
* @param workerId 工作机器ID
* @param dataCenterId 数据中心ID
*/
public Snowflake(long workerId, long dataCenterId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format("WorkerID不能超过%d且不能小于0", MAX_WORKER_ID));
}
if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
throw new IllegalArgumentException(String.format("DataCenterID不能超过%d且不能小于0", MAX_DATA_CENTER_ID));
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
/**
* 生成ID
* @return long类型的ID
*/
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 如果当前时间小于上次生成ID的时间戳,说明系统时钟回退过,抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("系统时钟回退,拒绝生成ID,上次生成ID的时间戳:%d,当前时间戳:%d",
lastTimestamp, timestamp));
}
// 如果当前时间等于上次生成ID的时间戳(同一毫秒内),则序列号加1
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {
// 如果序列号已经超过最大值,需要等待到下一毫秒再继续生成ID
timestamp = waitNextMillis(timestamp);
}
} else {
sequence = 0L;
}
// 更新上次生成ID的时间戳
lastTimestamp = timestamp;
// 生成ID
return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) |
(dataCenterId << DATA_CENTER_ID_SHIFT) |
(workerId << WORKER_ID_SHIFT) |
sequence;
}
/**
* 等待下一毫秒
* @param timestamp 上次生成ID的时间戳
* @return 下一毫秒的时间戳
*/
private long waitNextMillis(long timestamp) {
long nextTimestamp = System.currentTimeMillis();
while (nextTimestamp <= timestamp) {
nextTimestamp = System.currentTimeMillis();
}
return nextTimestamp;
}
// 示例
public static void main(String[] args) {
Snowflake snowflake = new Snowflake(1, 1);
System.out.println(snowflake.nextId());
}
}
在上述代码中,可以通过调整START_TIMESTAMP、WORKER_ID_BITS、DATA_CENTER_ID_BITS、SEQUENCE_BITS等参数来满足不同的需求,例如支持更多的机器、更高的QPS等。
这是批量生成的ID: