Twitter-Snowflake算法產生的背景至關簡單,爲了知足Twitter每秒上萬條消息的請求,每條消息都必須分配一條惟一的id,這些id還須要一些大體的順序(方便客戶端排序),而且在分佈式系統中不一樣機器產生的id必須不一樣。算法
snowflake把時間戳,工做機器id,序列號組合在一塊兒。less
除了最高位bit標記爲不可用之外,其他三組bit佔位都可浮動,看具體的業務需求而定。如下關於此算法的可行性研究分佈式
Console.WriteLine("41bit的時間戳能夠支持該算法使用年限:{0}", (1L << 41) / (3600L * 24 * 365 * 1000.0)); Console.WriteLine("10bit的工做機器id數量:{0}", (1L << 10) - 1); Console.WriteLine("12bit序列id數量:{0}", (1L << 12) - 1);
運行結果:spa
41bit的時間戳能夠支持該算法使用年限:69.7305700010147 10bit的工做機器id數量:1023 12bit序列id數量:4095
默認狀況下41bit的時間戳(從當前開始計算)能夠支持該算法使用近70年,10bit的工做機器id能夠支持1023臺機器,序列號支持1毫秒產生4095個自增序列id。那麼理論上,一個應用1秒鐘能夠產生409萬條自增ID,此算法可持續使用近70年。徹底能知足咱們平常開發項目的需求。code
工做機器id嚴格意義上來講這個bit段的使用能夠是進程級,機器級的話你可使用MAC地址來惟一標示工做機器,工做進程級可使用IP+Path來區分工做進程。若是工做機器比較少,可使用配置文件來設置這個id是一個不錯的選擇,若是機器過多配置文件的維護是一個災難性的事情。這個工做機器id的bit段也能夠進一步拆分,好比用前5個bit標記workerid,後5個bit標記datacenterid,具體代碼以下:orm
class Snowflake { //工做機器id的bit段拆分爲前5個bit標記workerid,後5個bit標記datacenterid const int WorkerIdBits = 5; const int DatacenterIdBits = 5; //序列號bit數 const int SequenceBits = 12; //最大編號限制 const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); private const long SequenceMask = -1L ^ (-1L << SequenceBits); //位左運算移動量 public const int WorkerIdShift = SequenceBits; public const int DatacenterIdShift = SequenceBits + WorkerIdBits; public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; //序列號記錄 private long _sequence = 0L; //時間戳記錄 private long _lastTimestamp = -1L; public long WorkerId { get; protected set; } public long DatacenterId { get; protected set; } public Snowflake(long workerId, long datacenterId, long sequence = 0L) { WorkerId = workerId; DatacenterId = datacenterId; _sequence = sequence; // sanity check for workerId if (workerId > MaxWorkerId || workerId < 0) { throw new ArgumentException( String.Format("worker Id can't be greater than {0} or less than 0", MaxWorkerId) ); } if (datacenterId > MaxDatacenterId || datacenterId < 0) { throw new ArgumentException( String.Format("datacenter Id can't be greater than {0} or less than 0", MaxDatacenterId)); } } /// <summary> /// 格林時間戳 /// </summary> /// <returns></returns> public long TimeGen() { DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);// return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds; } readonly object _lock = new Object(); public virtual long NextId() { lock (_lock) { var timestamp = TimeGen(); if (timestamp < _lastTimestamp) { throw new InvalidSystemClock(String.Format( "發現最新時間戳少{0}毫秒的異常", _lastTimestamp - timestamp)); } if (_lastTimestamp == timestamp) { _sequence = (_sequence + 1) & SequenceMask; if (_sequence == 0) { //序列號超過限制,從新取時間戳 timestamp = TilNextMillis(_lastTimestamp); } } else { _sequence = 0; } _lastTimestamp = timestamp; //snowflake算法 var id = (timestamp << TimestampLeftShift) | (DatacenterId << DatacenterIdShift) | (WorkerId << WorkerIdShift) | _sequence; return id; } } /// <summary> /// 從新取時間戳 /// </summary> protected virtual long TilNextMillis(long lastTimestamp) { var timestamp = TimeGen(); while (timestamp <= lastTimestamp) { //新的時間戳要大於舊的時間戳,纔算做有效時間戳 timestamp = TimeGen(); } return timestamp; } }