分布式ID生成SnowflakeId雪花算法和百度UidGenerator工具类

发布于:2025-06-28 ⋅ 阅读:(16) ⋅ 点赞:(0)

1:SnowflakeId雪花算法

public class SnowflakeIdUtils {
    @Getter
    private static final SnowflakeIdUtils instance = new SnowflakeIdUtils(0, 0);
    /**
     * 机器id所占的位数
     */
    private final long workerIdBits = 5L;
    /**
     * 数据标识id所占的位数
     */
    private final long datacenterIdBits = 5L;
    /**
     * 工作机器ID(0~31)
     */
    private final long workerId;
    /**
     * 数据中心ID(0~31)
     */
    private final long datacenterId;
    /**
     * 毫秒内序列(0~4095)
     */
    private long sequence = 0L;
    /**
     * 上次生成ID的时间截
     */
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     *
     * @param workerId     工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    private SnowflakeIdUtils(long workerId, long datacenterId) {
        long maxWorkerId = ~(-1L << workerIdBits);
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        long maxDatacenterId = ~(-1L << datacenterIdBits);
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        long st = System.currentTimeMillis();
        for (int i = 0; i < 4096000; i++) {
            SnowflakeIdUtils.getInstance().nextId();
        }
        long ent = System.currentTimeMillis();
        System.out.println("耗时=>{}" + (ent - st) + " 毫秒");
    }

    /**
     * 获得下一个ID (该方法是线程安全的)
     *
     * @return SnowflakeId
     */
    private synchronized long getSnowflakeId() {
        long timestamp = timeGen();
        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        //如果是同一时间生成的,则进行毫秒内序列
        long sequenceBits = 12L;
        if (lastTimestamp == timestamp) {
            //生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
            long sequenceMask = ~(-1L << sequenceBits);
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }
        //上次生成ID的时间截
        lastTimestamp = timestamp;
        //移位并通过或运算拼到一起组成64位的ID
        long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        long datacenterIdShift = sequenceBits + workerIdBits;
        long epoch = 1420041600000L;
        return ((timestamp - epoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << sequenceBits) | sequence;
    }

    public String nextId() {
        return LocalDate.now().toString().replace("-", "") + getSnowflakeId();
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     *
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
}

2:百度UidGenerator
 

public class UidGeneratorUtils {
    @Getter
    private static final UidGeneratorUtils instance = new UidGeneratorUtils(0);
    // 起始时间戳(2016-05-05 00:00:00)
    private final static long START_STAMP = 1462272000000L;

    // 序列号位数
    private final static long SEQUENCE_BIT = 12;
    // 工作节点位数
    private final static long WORKER_BIT = 10;

    // 掩码(用于取低 n 位)
    private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BIT);

    // 最大值
    private final static long MAX_WORKER_ID = ~(-1L << WORKER_BIT);

    // 工作节点 ID(需确保分布式环境中唯一)
    private final long workerId;

    // 毫秒内序列号(从 0 开始)
    private long sequence = 0L;

    // 上一次时间戳
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     *
     * @param workerId 工作节点 ID(0~1023)
     */
    public UidGeneratorUtils(long workerId) {
        if (workerId < 0 || workerId > MAX_WORKER_ID) {
            throw new IllegalArgumentException("Worker ID 必须在 0~" + MAX_WORKER_ID + " 之间");
        }
        this.workerId = workerId;
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        long st = System.currentTimeMillis();
        for (int i = 0; i < 4096000; i++) {
            System.out.println(getInstance().nextId());
        }
        long ent = System.currentTimeMillis();
        System.out.println("耗时=>{}" + (ent - st) + " 毫秒");
    }

    /**
     * 生成 ID
     *
     * @return 64 位 long 型 ID
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        // 时间回拨处理
        if (timestamp < lastTimestamp) {
            long offset = lastTimestamp - timestamp;
            if (offset > 5) {  // 允许最多 5ms 的回拨
                throw new RuntimeException("时钟回拨,拒绝生成 ID。回拨时间:" + offset + "ms");
            }
            timestamp = lastTimestamp;  // 等待时间前进
        }

        // 同一毫秒内生成 ID
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);  // 等待下一毫秒
            }
        } else {
            sequence = 0;  // 新毫秒,重置序列号
        }

        lastTimestamp = timestamp;

        // 拼接 ID:符号位(0) + 时间戳 + 工作节点 ID + 序列号
        return (timestamp - START_STAMP) << (WORKER_BIT + SEQUENCE_BIT) | (workerId << SEQUENCE_BIT) | sequence;
    }

    /**
     * 等待到下一毫秒
     *
     * @param lastTimestamp 上一次时间戳
     * @return 当前时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 获取当前时间戳
     *
     * @return 毫秒级时间戳
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }
}