Twitter的分布式自增ID算法snowflake - C#版

概述

分布式系統(tǒng)中,有一些需要使用全局唯一ID的場景,這種時候為了防止ID沖突可以使用36位的UUID,但是UUID有一些缺點,首先他相對比較長,另外UUID一般是無序的。有些時候我們希望能使用一種簡單一些的ID,并且希望ID能夠按照時間有序生成。而twitter的snowflake解決了這種需求,最初Twitter把存儲系統(tǒng)從MySQL遷移到Cassandra,因為Cassandra沒有順序ID生成機制,所以開發(fā)了這樣一套全局唯一ID生成服務(wù)。

該項目地址為:https://github.com/twitter/snowflake是用Scala實現(xiàn)的。

python版詳見開源項目https://github.com/erans/pysnowflake。

結(jié)構(gòu)

snowflake的結(jié)構(gòu)如下(每部分用-分開):

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

第一位為未使用,接下來的41位為毫秒級時間(41位的長度可以使用69年),然后是5位datacenterId和5位workerId(10位的長度最多支持部署1024個節(jié)點) ,最后12位是毫秒內(nèi)的計數(shù)(12位的計數(shù)順序號支持每個節(jié)點每毫秒產(chǎn)生4096個ID序號)

一共加起來剛好64位,為一個Long型。(轉(zhuǎn)換成字符串長度為18)

snowflake生成的ID整體上按照時間自增排序,并且整個分布式系統(tǒng)內(nèi)不會產(chǎn)生ID碰撞(由datacenter和workerId作區(qū)分),并且效率較高。據(jù)說:snowflake每秒能夠產(chǎn)生26萬個ID。

源碼(C#版本源碼)

public class IdWorker

? ? {

? ? ? ? //機器ID

? ? ? ? private static long workerId;

? ? ? ? private static long twepoch = 687888001020L; //唯一時間,這是一個避免重復(fù)的隨機量,自行設(shè)定不要大于當(dāng)前時間戳

? ? ? ? private static long sequence = 0L;

? ? ? ? private static int workerIdBits = 4; //機器碼字節(jié)數(shù)。4個字節(jié)用來保存機器碼(定義為Long類型會出現(xiàn),最大偏移64位,所以左移64位沒有意義)

? ? ? ? public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大機器ID

? ? ? ? private static int sequenceBits = 10; //計數(shù)器字節(jié)數(shù),10個字節(jié)用來保存計數(shù)碼

? ? ? ? private static int workerIdShift = sequenceBits; //機器碼數(shù)據(jù)左移位數(shù),就是后面計數(shù)器占用的位數(shù)

? ? ? ? private static int timestampLeftShift = sequenceBits + workerIdBits; //時間戳左移動位數(shù)就是機器碼和計數(shù)器總字節(jié)數(shù)

? ? ? ? public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒內(nèi)可以產(chǎn)生計數(shù),如果達到該值則等到下一微妙在進行生成

? ? ? ? private long lastTimestamp = -1L;

? ? ? ? /// <summary>

? ? ? ? /// 機器碼

? ? ? ? /// </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; //用&運算計算該微秒內(nèi)產(chǎn)生的計數(shù)是否已經(jīng)到達上限

? ? ? ? ? ? ? ? ? ? if (IdWorker.sequence == 0)

? ? ? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ? ? //一微妙內(nèi)產(chǎn)生的ID計數(shù)已達上限,等待下一微妙

? ? ? ? ? ? ? ? ? ? ? ? timestamp = tillNextMillis(this.lastTimestamp);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? else

? ? ? ? ? ? ? ? { //不同微秒生成ID

? ? ? ? ? ? ? ? ? ? IdWorker.sequence = 0; //計數(shù)清0

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? if (timestamp < lastTimestamp)

? ? ? ? ? ? ? ? { //如果當(dāng)前時間戳比上一次生成ID時時間戳還小,拋出異常,因為不能保證現(xiàn)在生成的ID之前沒有生成過

? ? ? ? ? ? ? ? ? ? throw new Exception(string.Format("Clock moved backwards.? Refusing to generate id for {0} milliseconds",

? ? ? ? ? ? ? ? ? ? ? ? this.lastTimestamp - timestamp));

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? this.lastTimestamp = timestamp; //把當(dāng)前時間戳保存為最后生成ID的時間戳

? ? ? ? ? ? ? ? long nextId = (timestamp - twepoch << timestampLeftShift) | IdWorker.workerId << IdWorker.workerIdShift | IdWorker.sequence;

? ? ? ? ? ? ? ? return nextId;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? /// <summary>

? ? ? ? /// 獲取下一微秒時間戳

? ? ? ? /// </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)前時間戳

? ? ? ? /// </summary>

? ? ? ? /// <returns></returns>

? ? ? ? private long timeGen()

? ? ? ? {

? ? ? ? ? ? return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;

? ? ? ? }

? ? }

調(diào)用方法:

IdWorker idworker = new IdWorker(1);

? ? ? ? ? ? for (int i = 0; i < 1000; i++)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Response.Write(idworker.nextId() + "<br/>");

? ? ? ? ? ? }

代碼下載地址:下載

snowflate算法說明

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

作者:doubleicon

來源:CSDN

原文:https://blog.csdn.net/u011872945/article/details/54562213

版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容