概述
分布式系統(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/>");
? ? ? ? ? ? }
代碼下載地址:下載
---------------------
作者:doubleicon
來源:CSDN
原文:https://blog.csdn.net/u011872945/article/details/54562213
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!