Twitter雪花算法 SnowFlake算法 的java實現


概述
SnowFlake算法是Twitter設計的一個可以在分布式系統中生成唯一的ID的算法,它可以滿足Twitter每秒上萬條消息ID分配的請求,這些消息ID是唯一的且有大致的遞增順序。

原理
SnowFlake算法產生的ID是一個64位的整型,結構如下(每一部分用“-”符號分隔):

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

1位標識部分,在java中由於long的最高位是符號位,正數是0,負數是1,一般生成的ID為正數,所以為0;
41位時間戳部分,這個是毫秒級的時間,一般實現上不會存儲當前的時間戳,而是時間戳的差值(當前時間-固定的開始時間),這樣可以使產生的ID從更小值開始;41位的時間戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年;
10位節點部分,Twitter實現中使用前5位作為數據中心標識,后5位作為機器標識,可以部署1024個節點;
12位序列號部分,支持同一毫秒內同一個節點可以生成4096個ID;

SnowFlake算法生成的ID大致上是按照時間遞增的,用在分布式系統中時,需要注意數據中心標識和機器標識必須唯一,這樣就能保證每個節點生成的ID都是唯一的。或許我們不一定都需要像上面那樣使用5位作為數據中心標識,5位作為機器標識,可以根據我們業務的需要,靈活分配節點部分,如:若不需要數據中心,完全可以使用全部10位作為機器標識;若數據中心不多,也可以只使用3位作為數據中心,7位作為機器標識。


源碼

/**
* twitter的snowflake算法 -- java實現
*
* @author rock
* @date 2016/11/26
*/
public class SnowFlake {
 
    /**
    * 起始的時間戳
    */
    private final static long START_STMP = 1480166465631L;
 
    /**
    * 每一部分占用的位數
    */
    private final static long SEQUENCE_BIT = 12; //序列號占用的位數
    private final static long MACHINE_BIT = 5;  //機器標識占用的位數
    private final static long DATACENTER_BIT = 5;//數據中心占用的位數
 
    /**
    * 每一部分的最大值
    */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
 
    /**
    * 每一部分向左的位移
    */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
 
    private long datacenterId;  //數據中心
    private long machineId;    //機器標識
    private long sequence = 0L; //序列號
    private long lastStmp = -1L;//上一次時間戳
 
    public SnowFlake(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }
 
    /**
    * 產生下一個ID
    *
    * @return
    */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }
 
        if (currStmp == lastStmp) {
            //相同毫秒內,序列號自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列數已經達到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒內,序列號置為0
            sequence = 0L;
        }
 
        lastStmp = currStmp;
 
        return (currStmp - START_STMP) << TIMESTMP_LEFT //時間戳部分
                | datacenterId << DATACENTER_LEFT      //數據中心部分
                | machineId << MACHINE_LEFT            //機器標識部分
                | sequence;                            //序列號部分
    }
 
    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }
 
    private long getNewstmp() {
        return System.currentTimeMillis();
    }
 
}

 可以寫一代碼測試一下,如下所示:

public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(2, 3);
        for (int i = 0; i < (1 << 12); i++) {
            System.out.println(snowFlake.nextId());
        }
 
    }

 循環生成2^12個ID,運行結果如下:

...
2099698216995
2099698216996
2099698216997
2099698216998
2099698216999
2099698217000
2099698217001
2099698217002
2099698217003
2099698217004
2099698217005
2099698217006
2099698217007
2099698217008
2099698217009
2099698217010
2099698217011
2099698217012
2099698217013
2099698217014
2099698217015
2099698217016
2099698217017
2099698217018
2099698217019
2099698217020
2099698217021
2099698217022
2099698217023
2099698217024
2099698217025
2099698217026
2099698217027
2099698217028
2099698217029
2099698217030
2099698217031
2099702411264
2099702411265
2099702411266
2099702411267
2099702411268
2099702411269
2099702411270
2099702411271
2099702411272
2099702411273
2099702411274
2099702411275
2099702411276
2099702411277
...

 

可以看到生成的ID都是遞增的,而且都是唯一的。
源碼已經提交在GitHub: https://github.com/beyondfengyu/SnowFlake


免責聲明!

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



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