java 雪花算法


SnowFlake 算法,是 Twitter 開源的分布式 id 生成算法。

其核心思想是,使用一個 64 bit 的 long 型的數字作為全局唯一 id。

 

這個64 bit 的 long 型數字的儲存模型如下:

第一部分,占用 1 bit:0。

第二部分,占用 41 bit:表示的是時間戳。

第三部分,占用 5  bit:表示的是機房 id。

第四部分,占用 5  bit:表示的是機器 id。

第五部分,占用 12  bit:表示的是序號。

 

代碼如下:

package com.demo.springboot.common;

public class IdWorker {
    //因為二進制里第一個 bit 為如果是 1,那么都是負數,但是我們生成的 id 都是正數,所以第一個 bit 統一都是 0。
    //代表一毫秒內生成的多個id的最新序號 2進制12位 2^12 - 1 = 4095個
    private long sequence;
    //機器ID 2進制5位 2^5 - 1 = 31個
    private long workerId;
    //機房ID 2進制5位 2^5 - 1 = 31個
    private long datacenterId;
    //設置一個時間初始值 2進制41位 2^41 - 1差不多可以用69年
    //2021-01-01 => 1609430400000
    private long epoch = 1609430400000L;
    //12位的最新序號
    private long sequenceBits = 12L;
    //5位的機器id
    private long workerIdBits = 5L;
    //5位的機房id
    private long datacenterIdBits = 5L;

    //這個是二進制運算,就是5 bit機器id最大值31
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    //這個是一個意思,就是5 bit機房id最大值31
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    private long sequenceMask = -1L ^ (-1L << sequenceBits);
    private long workerIdShift = sequenceBits;
    private long datacenterIdShift = sequenceBits + workerIdBits;
    private long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;

    //記錄產生時間毫秒數,判斷是否是同1毫秒
    private long lastTimestamp = -1L;

    public IdWorker(long datacenterId, long workerId, long sequence) {
        //檢查機房id和機器id 不能超過31 不能小於0
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        this.datacenterId = datacenterId;
        this.workerId = workerId;
        this.sequence = sequence;
    }

    //生成一個全局唯一的id
    public synchronized long nextId() {
        //獲取當前時間戳,單位毫秒
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            System.err.printf(
                    "clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
            throw new RuntimeException(
                    String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
                            lastTimestamp - timestamp));
        }
        //同一個毫秒內生成多個id,seqence序號遞增1,最大是4095
        if (lastTimestamp == timestamp) {
            //這個位運算避免sequence超過4095
            sequence = (sequence + 1) & sequenceMask;
            //當某一毫秒產生的id數超過4095,系統會進入等待,直到下一毫秒繼續產生id
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        //記錄一下最近一次生成id的時間戳,單位毫秒
        lastTimestamp = timestamp;
        //二進制位運算操作,生成一個64bit的id
        return ((timestamp - epoch) << timestampShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) | sequence;
    }

    //當某一毫秒產生的id數超過4095,系統會進入等待,直到下一毫秒繼續產生id
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    //獲取當前時間戳
    private long timeGen(){
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        IdWorker worker = new IdWorker(1,1,1);
        for (int i = 0; i < 10; i++) {
            System.out.println(worker.nextId());
        }
    }

}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM