Twitter的分布式自增ID算法snowflake(雪花算法) C#和Java版

你瞅啥絮供?

Twitter的snowflake解決了能夠按照時(shí)間生成有序且不重復(fù)的全局唯一ID衣吠!

--- C# 版本源碼:

public class IdWorker
    {
        //機(jī)器ID
        private static long workerId;
        private static long twepoch = 687888001020L; //唯一時(shí)間,這是一個(gè)避免重復(fù)的隨機(jī)量壤靶,自行設(shè)定不要大于當(dāng)前時(shí)間戳
        private static long sequence = 0L;
        private static int workerIdBits = 4; //機(jī)器碼字節(jié)數(shù)缚俏。4個(gè)字節(jié)用來(lái)保存機(jī)器碼(定義為L(zhǎng)ong類(lèi)型會(huì)出現(xiàn),最大偏移64位贮乳,所以左移64位沒(méi)有意義)
        public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大機(jī)器ID
        private static int sequenceBits = 10; //計(jì)數(shù)器字節(jié)數(shù)忧换,10個(gè)字節(jié)用來(lái)保存計(jì)數(shù)碼
        private static int workerIdShift = sequenceBits; //機(jī)器碼數(shù)據(jù)左移位數(shù),就是后面計(jì)數(shù)器占用的位數(shù)
        private static int timestampLeftShift = sequenceBits + workerIdBits; //時(shí)間戳左移動(dòng)位數(shù)就是機(jī)器碼和計(jì)數(shù)器總字節(jié)數(shù)
        public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒內(nèi)可以產(chǎn)生計(jì)數(shù)向拆,如果達(dá)到該值則等到下一微妙在進(jìn)行生成
        private long lastTimestamp = -1L;

        /// <summary>
        /// 機(jī)器碼
        /// </summary>
        /// <param name="workerId"></param>
        public IdWorker(long workerId)
        {
            if (workerId > maxWorkerId || workerId < 0)
                throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0 ", workerId));
            IdWorker.workerId = workerId;
        }

        public long nextId()
        {
            lock (this)
            {
                long timestamp = timeGen();
                if (this.lastTimestamp == timestamp)
                { //同一微妙中生成ID
                    IdWorker.sequence = (IdWorker.sequence + 1) & IdWorker.sequenceMask; //用&運(yùn)算計(jì)算該微秒內(nèi)產(chǎn)生的計(jì)數(shù)是否已經(jīng)到達(dá)上限
                    if (IdWorker.sequence == 0)
                    {
                        //一微妙內(nèi)產(chǎn)生的ID計(jì)數(shù)已達(dá)上限亚茬,等待下一微妙
                        timestamp = tillNextMillis(this.lastTimestamp);
                    }
                }
                else
                { //不同微秒生成ID
                    IdWorker.sequence = 0; //計(jì)數(shù)清0
                }
                if (timestamp < lastTimestamp)
                { //如果當(dāng)前時(shí)間戳比上一次生成ID時(shí)時(shí)間戳還小,拋出異常浓恳,因?yàn)椴荒鼙WC現(xiàn)在生成的ID之前沒(méi)有生成過(guò)
                    throw new Exception(string.Format("Clock moved backwards.  Refusing to generate id for {0} milliseconds",
                        this.lastTimestamp - timestamp));
                }
                this.lastTimestamp = timestamp; //把當(dāng)前時(shí)間戳保存為最后生成ID的時(shí)間戳
                long nextId = (timestamp - twepoch << timestampLeftShift) | IdWorker.workerId << IdWorker.workerIdShift | IdWorker.sequence;
                return nextId;
            }
        }

        /// <summary>
        /// 獲取下一微秒時(shí)間戳
        /// </summary>
        /// <param name="lastTimestamp"></param>
        /// <returns></returns>
        private long tillNextMillis(long lastTimestamp)
        {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp)
            {
                timestamp = timeGen();
            }
            return timestamp;
        }

        /// <summary>
        /// 生成當(dāng)前時(shí)間戳
        /// </summary>
        /// <returns></returns>
        private long timeGen()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }
    }

C#調(diào)用方法:

      IdWorker idworker = new IdWorker(1);
      for (int i = 0; i < 1000; i++)
      {
           Response.Write(idworker.nextId() + "<br/>");
      }

--- java 版本源碼:

public class IdWorker {
    private final long workerId;
    private final static long twepoch = 1288834974657L;
    private long sequence = 0L;
    private final static long workerIdBits = 4L;
    public final static long maxWorkerId = -1L ^ -1L << workerIdBits;
    private final static long sequenceBits = 10L;
    private final static long workerIdShift = sequenceBits;
    private final static long timestampLeftShift = sequenceBits + workerIdBits;
    public final static long sequenceMask = -1L ^ -1L << sequenceBits;
    private long lastTimestamp = -1L;
    public IdWorker(final long workerId) {
        super();
        if (workerId > this.maxWorkerId || workerId < 0) {
              throw new IllegalArgumentException(String.format(
                                          "worker Id can't be greater than %d or less than 0",
                    this.maxWorkerId));
        }
        this.workerId = workerId;
    }
    public synchronized long nextId() {
        long timestamp = this.timeGen();
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & this.sequenceMask;
            if (this.sequence == 0) {
                System.out.println("###########" + sequenceMask);
                timestamp = this.tilNextMillis(this.lastTimestamp);
             }
            } else {
                this.sequence = 0;
            }
            if (timestamp < this.lastTimestamp) {
                try {
                    throw new Exception(
                          String.format(
                              "Clock moved backwards. Refusing to generate id for %d milliseconds",
                              this.lastTimestamp - timestamp));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
 
        this.lastTimestamp = timestamp;
        long nextId = ((timestamp - twepoch << timestampLeftShift))
              | (this.workerId << this.workerIdShift) | (this.sequence);
        System.out.println("timestamp:" + timestamp + ",timestampLeftShift:"
                                      + timestampLeftShift + ",nextId:" + nextId + ",workerId:"
                                      + workerId + ",sequence:" + sequence);
        return nextId;
    }
 
    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }
 
    private long timeGen() {
        return System.currentTimeMillis();
    }
 
    //調(diào)用
    public static void main(String[] args){
        IdWorker worker2 = new IdWorker(2);
        System.out.println(worker2.nextId());
    }
}

參考1
參考2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刹缝,一起剝皮案震驚了整個(gè)濱河市碗暗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梢夯,老刑警劉巖言疗,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異颂砸,居然都是意外死亡噪奄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)人乓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)勤篮,“玉大人,你說(shuō)我怎么就攤上這事撒蟀⌒鸾鳎” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵保屯,是天一觀(guān)的道長(zhǎng)手负。 經(jīng)常有香客問(wèn)我,道長(zhǎng)姑尺,這世上最難降的妖魔是什么竟终? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮切蟋,結(jié)果婚禮上统捶,老公的妹妹穿的比我還像新娘。我一直安慰自己柄粹,他們只是感情好喘鸟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著驻右,像睡著了一般什黑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上堪夭,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天愕把,我揣著相機(jī)與錄音,去河邊找鬼森爽。 笑死恨豁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的爬迟。 我是一名探鬼主播橘蜜,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼付呕!你這毒婦竟也來(lái)了扮匠?” 一聲冷哼從身側(cè)響起捧请,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凡涩,失蹤者是張志新(化名)和其女友劉穎棒搜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體活箕,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡力麸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了育韩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片克蚂。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖筋讨,靈堂內(nèi)的尸體忽然破棺而出埃叭,到底是詐尸還是另有隱情,我是刑警寧澤悉罕,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布赤屋,位于F島的核電站,受9級(jí)特大地震影響壁袄,放射性物質(zhì)發(fā)生泄漏类早。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一嗜逻、第九天 我趴在偏房一處隱蔽的房頂上張望涩僻。 院中可真熱鬧,春花似錦栈顷、人聲如沸逆日。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)室抽。三九已至,卻和暖如春蛙卤,著一層夾襖步出監(jiān)牢的瞬間狠半,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工颤难, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留神年,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓行嗤,卻偏偏與公主長(zhǎng)得像已日,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栅屏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容