📅  最后修改于: 2023-12-03 15:42:25.651000             🧑  作者: Mango
在分布式系统中,通常需要产生全局唯一的ID。而雪花算法是一种经典的全局唯一ID生成算法。它的核心思想是将一个64位的long值分为多个部分来生成ID,包括时间戳、数据中心ID、机器ID和序列号。其中,时间戳占用了42位,数据中心ID占用了5位,机器ID占用了5位,序列号占用了12位。
那么,如果我们要查询雪花算法生成的最后一个ID,应该如何实现呢?下面是一个Java代码片段供参考:
import java.util.concurrent.atomic.AtomicLong;
public class SnowFlake {
/* 时间戳所占的位数 */
private static final long TIMESTAMP_BITS = 42L;
/* 数据中心 ID 所占的位数 */
private static final long DATA_CENTER_ID_BITS = 5L;
/* 机器 ID 所占的位数 */
private static final long WORKER_ID_BITS = 5L;
/* 序列号所占的位数 */
private static final long SEQUENCE_BITS = 12L;
/* 数据中心 ID 左移位数 */
private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + TIMESTAMP_BITS;
/* 机器 ID 左移位数 */
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS + TIMESTAMP_BITS;
/* 时间戳左移位数 */
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
/* 序列号掩码 */
private static final long SEQUENCE_MASK = (1L << SEQUENCE_BITS) - 1L;
/* 上一个生成ID的时间戳 */
private static long lastTimestamp = -1L;
/* 数据中心 ID */
private final long dataCenterId;
/* 机器 ID */
private final long workerId;
/* 序列号 */
private final AtomicLong sequence = new AtomicLong(0L);
public SnowFlake(long dataCenterId, long workerId) {
if (dataCenterId > ((1L << DATA_CENTER_ID_BITS) - 1L)) {
throw new IllegalArgumentException("Data center ID can't be greater than " + ((1L << DATA_CENTER_ID_BITS) - 1L));
}
if (workerId > ((1L << WORKER_ID_BITS) - 1L)) {
throw new IllegalArgumentException("Worker ID can't be greater than " + ((1L << WORKER_ID_BITS) - 1L));
}
this.dataCenterId = dataCenterId;
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate ID for " + (lastTimestamp - timestamp) + " milliseconds.");
}
if (timestamp == lastTimestamp) {
long currentSequence = sequence.incrementAndGet();
if (currentSequence > SEQUENCE_MASK) {
lastTimestamp = untilNextMillis(lastTimestamp);
sequence.set(0L);
}
return ((lastTimestamp << TIMESTAMP_LEFT_SHIFT) |
(dataCenterId << DATA_CENTER_ID_SHIFT) |
(workerId << WORKER_ID_SHIFT) |
(currentSequence));
} else {
lastTimestamp = timestamp;
sequence.set(0L);
return ((lastTimestamp << TIMESTAMP_LEFT_SHIFT) |
(dataCenterId << DATA_CENTER_ID_SHIFT) |
(workerId << WORKER_ID_SHIFT) |
(sequence.incrementAndGet()));
}
}
private long untilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
public static void main(String[] args) {
SnowFlake snowFlake = new SnowFlake(1L, 1L);
for (int i = 0; i < 10; i++) {
System.out.println(snowFlake.nextId());
}
}
}
此代码是一个简单的雪花算法实现,可以生成全局唯一的ID。如果要查询最后一个ID,可以将上一个生成ID的时间戳记录下来,然后遍历生成的ID序列,直到找到最后一个ID,代码如下:
SnowFlake snowFlake = new SnowFlake(1L, 1L);
long lastTimestamp = -1L;
long lastId = -1L;
for (int i = 0; i < 10000000; i++) {
long id = snowFlake.nextId();
if (id < lastId) {
break;
}
lastId = id;
lastTimestamp = id >> SnowFlake.TIMESTAMP_LEFT_SHIFT;
}
System.out.println("Last ID generated by SnowFlake: " + lastId);
System.out.println("Timestamp of last ID: " + new Date(lastTimestamp));
这段代码会生成10000000个ID,并记录下最后一个ID的时间戳和值。注意,在实际应用中,这种遍历方式可能会耗费较长时间,因此应该考虑其他更高效的方法来处理此类问题。