📜  雪花最后一个查询 id (1)

📅  最后修改于: 2023-12-03 15:42:25.651000             🧑  作者: Mango

雪花最后一个查询 ID

在分布式系统中,通常需要产生全局唯一的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的时间戳和值。注意,在实际应用中,这种遍历方式可能会耗费较长时间,因此应该考虑其他更高效的方法来处理此类问题。