首頁 > 軟體

Java 通過手寫分散式雪花SnowFlake生成ID方法詳解

2022-04-07 19:00:53

SnowFlake演演算法

SnowFlake演演算法生成id的結果是一個64bit大小的整數,它的結構如下圖:

分為四段:

第一段: 1位為未使用,永遠固定為0。

(因為二進位制中最高位是符號位,1表示負數,0表示正數。生成的id一般都是用正整數,所以最高位固定為0 )

第二段: 41位為毫秒級時間(41位的長度可以使用69年)

第三段: 10位為workerId(10位的長度最多支援部署1024個節點)

(這裡的10位又分為兩部分,第一部分5位表示資料中心ID(0-31)第二部分5位表示機器ID(0-31))

第四段: 12位元為毫秒內的計數(12位元的計數順序號支援每個節點每毫秒產生4096個ID序號)

程式碼實現:

import java.util.HashSet;
import java.util.concurrent.atomic.AtomicLong;

public class SnowFlake {

    //時間 41位
    private static long lastTime = System.currentTimeMillis();

    //資料中心ID 5位(預設0-31)
    private long datacenterId = 0;
    private long datacenterIdShift = 5;

    //機房機器ID 5位(預設0-31)
    private long workerId = 0;
    private long workerIdShift = 5;

    //亂數 12位元(預設0~4095)
    private AtomicLong random = new AtomicLong();
    private long randomShift = 12;
    //亂數的最大值
    private long maxRandom = (long) Math.pow(2, randomShift);

    public SnowFlake() {
    }

    public SnowFlake(long workerIdShift, long datacenterIdShift){
        if (workerIdShift < 0 ||
                datacenterIdShift < 0 ||
                workerIdShift + datacenterIdShift > 22) {
            throw new IllegalArgumentException("引數不匹配");
        }
        this.workerIdShift = workerIdShift;
        this.datacenterIdShift = datacenterIdShift;
        this.randomShift = 22 - datacenterIdShift - workerIdShift;
        this.maxRandom = (long) Math.pow(2, randomShift);
    }

    //獲取雪花的ID
    private long getId() {
        return lastTime << (workerIdShift + datacenterIdShift + randomShift) |
                workerId << (datacenterIdShift + randomShift) |
                datacenterId << randomShift |
                random.get();
    }

    //生成一個新的ID
    public synchronized long nextId() {
        long now = System.currentTimeMillis();

        //如果當前時間和上一次時間不在同一毫秒內,直接返回
        if (now > lastTime) {
            lastTime = now;
            random.set(0);
            return getId();
        }

	//將最後的亂數,進行+1操作
        if (random.incrementAndGet() < maxRandom) {
            return getId();
        }

        //自選等待下一毫秒
        while (now <= lastTime) {
            now = System.currentTimeMillis();
        }

        lastTime = now;
        random.set(0);
        return getId();

    }

    //測試
    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake();
        HashSet<Long> set = new HashSet<>();
        for (int i = 0; i < 10000; i++) {
            set.add(snowFlake.nextId());
        }
        System.out.println(set.size());
    }

}

程式碼中獲取id的方法利用位運算實現

 1  |                    41                        |  5  |   5  |     12      

   0|0001100 10100010 10111110 10001001 01011100 00|00000|0 0000|0000 00000000 //41位的時間

   0|000000‭0 00000000 00000000 00000000 00000000 00|10001|0 0000|0000 00000000 //5位的資料中心ID

   0|0000000 00000000 00000000 00000000 00000000 00|00000|1 1001|0000 00000000 //5為的機器ID

or 0|0000000 00000000 00000000 00000000 00000000 00|00000|0 0000|‭0000 00000000‬ //12位元的sequence

------------------------------------------------------------------------------------------

   0|0001100 10100010 10111110 10001001 01011100 00|10001|1 1001|‭0000 00000000‬ //結果:910499571847892992

SnowFlake優點

所有生成的id按時間趨勢遞增

整個分散式系統內不會產生重複id(因為有datacenterId和workerId來做區分)

SnowFlake不足

由於SnowFlake強依賴時間戳,所以時間的變動會造成SnowFlake的演演算法產生錯誤。

到此這篇關於Java 通過手寫分散式雪花SnowFlake生成ID方法詳解的文章就介紹到這了,更多相關Java SnowFlake內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com