首頁  >  文章  >  Java  >  Java怎麼透過手寫分散式雪花SnowFlake產生ID

Java怎麼透過手寫分散式雪花SnowFlake產生ID

PHPz
PHPz轉載
2023-04-24 21:34:16989瀏覽

SnowFlake演算法

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

Java怎麼透過手寫分散式雪花SnowFlake產生ID

##分為四段:

第一段: 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的方法利用位元運算實現

Java怎麼透過手寫分散式雪花SnowFlake產生ID

 1  |                    41         

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

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

or 0|0000000 00000000 00000000 00000000 0000000 00000000000 sequence

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

   0 |0001100 10100010 10111110 10001001 01011100 00|10001|1 1001|0000 00000000 //結果:9104995718479000 00000000增整個分佈式系統內不會產生重複id(因為有datacenterId和workerId來做區分) SnowFlake不足:

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

以上是Java怎麼透過手寫分散式雪花SnowFlake產生ID的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除