日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

分布式ID自增算法 Snowflake

發布時間:2023/12/4 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式ID自增算法 Snowflake 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

近在嘗試EF的多數據庫移植,但是原始項目中主鍵用的Sqlserver的GUID。MySQL沒法移植了。

其實發現GUID也沒法保證數據的遞增性,又不太想使用int遞增主鍵,就開始探索別的ID形式。

后來發現twitter的Snowflake算法。

一開始我嘗試過直接引用Nuget里的Snowflake的擴展包(有Framework版和Core版),不過有些Bug,就是初始化參數有的時候不一定好用,最大問題是,這個需要實例化對象,并且通過同一個對象來實生成ID,否則會出現ID沖突問題。而且,我們還要考慮對象在內存的生存問題。學習這種算法是夠用了,但是用到實際生產中則有很多問題,雖然我們可以通過一些技術來避免這種問題,但是總覺得不夠優雅,不符合我的美學!?

后來看到這篇博客?C# 實現 Snowflake算法?先感謝一下這個大神。但是同樣有上述的部分問題,做5線程的并發測試的時候效率不如擴展的。后面我們會提到。

我從這篇博客里摘來了源碼,對有的地方做了一些改動使得其更適合(至少我認為是)更適合生產環境。

?先貼源碼


public class SFID

? ? {

? ? ? ? /// <summary>

? ? ? ? /// 機器碼

? ? ? ? /// </summary>

? ? ? ? private static long _workerId;


? ? ? ? /// <summary>

? ? ? ? /// 初始基準時間戳,小于當前時間點即可

? ? ? ? /// 分布式項目請保持此時間戳一致

? ? ? ? /// </summary>

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


? ? ? ? /// <summary>

? ? ? ? /// 毫秒計數器

? ? ? ? /// </summary>

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


? ? ? ? /// <summary>

? ? ? ? /// 機器碼字節數。4個字節用來保存機器碼(定義為Long類型會出現,最大偏移64位,所以左移64位沒有意義)

? ? ? ? /// </summary>

? ? ? ? private static int workerIdBits = 4;?


? ? ? ? /// <summary>

? ? ? ? /// 最大機器ID所占的位數

? ? ? ? /// </summary>

? ? ? ? private static long maxWorkerId = -1L ^ -1L << workerIdBits;


? ? ? ? /// <summary>

? ? ? ? /// 計數器字節數,10個字節用來保存計數碼

? ? ? ? /// </summary>

? ? ? ? private static int sequenceBits = 12;


? ? ? ? /// <summary>

? ? ? ? /// 機器碼數據左移位數,就是后面計數器占用的位數

? ? ? ? /// </summary>

? ? ? ? private static int workerIdShift = sequenceBits;


? ? ? ? /// <summary>

? ? ? ? /// 時間戳左移動位數就是機器碼和計數器總字節數

? ? ? ? /// </summary>

? ? ? ? private static int timestampLeftShift = sequenceBits + workerIdBits;


? ? ? ? /// <summary>

? ? ? ? /// 一微秒內可以產生計數,如果達到該值則等到下一微妙在進行生成

? ? ? ? /// </summary>

? ? ? ? private static long sequenceMask = -1L ^ -1L << sequenceBits;


? ? ? ? /// <summary>

? ? ? ? /// 最后一次的時間戳

? ? ? ? /// </summary>

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


? ? ? ? /// <summary>

? ? ? ? /// 線程鎖對象

? ? ? ? /// </summary>

? ? ? ? private static object locker = new object();

? ? ? ??

? ? ? ? static SFID()

? ? ? ? {

? ? ? ? ? ? _workerId = new Random(DateTime.Now.Millisecond).Next(1, (int)maxWorkerId);

? ? ? ? ? ? _twepoch = timeGen(2010, 1, 1, 0, 0, 0);

? ? ? ? }


? ? ? ? /// <summary>

? ? ? ? /// 機器編號

? ? ? ? /// </summary>

? ? ? ? public static long WorkerID

? ? ? ? {

? ? ? ? ? ? get { return _workerId; }

? ? ? ? ? ? set

? ? ? ? ? ? {

? ? ? ? ? ? ? ? if (value > 0 && value < maxWorkerId)

? ? ? ? ? ? ? ? ? ? _workerId = value;

? ? ? ? ? ? ? ? else

? ? ? ? ? ? ? ? ? ? throw new Exception("Workerid must be greater than 0 or less than " + maxWorkerId);

? ? ? ? ? ? }

? ? ? ? }


? ? ? ? /// <summary>

? ? ? ? /// 獲取新的ID

? ? ? ? /// </summary>

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

? ? ? ? public static long NewID()

? ? ? ? {

? ? ? ? ? ? lock (locker)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? long timestamp = timeGen();

? ? ? ? ? ? ? ? if (lastTimestamp == timestamp)

? ? ? ? ? ? ? ? { //同一微妙中生成ID

? ? ? ? ? ? ? ? ? ? sequence = (sequence + 1) & sequenceMask; //用&運算計算該微秒內產生的計數是否已經到達上限

? ? ? ? ? ? ? ? ? ? if (sequence == 0)

? ? ? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ? ? //一微妙內產生的ID計數已達上限,等待下一微妙

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

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? else

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

? ? ? ? ? ? ? ? ? ? sequence = 0; //計數清0

? ? ? ? ? ? ? ? }

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

? ? ? ? ? ? ? ? {?

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

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

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? lastTimestamp = timestamp; //把當前時間戳保存為最后生成ID的時間戳

? ? ? ? ? ? ? ? return (timestamp - _twepoch << timestampLeftShift) | _workerId << workerIdShift | sequence;

? ? ? ? ? ? }

? ? ? ? }


? ? ? ? /// <summary>

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

? ? ? ? /// </summary>

? ? ? ? /// <param name="lastTimestamp"></param>

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

? ? ? ? private static long tillNextMillis(long lastTimestamp)

? ? ? ? {

? ? ? ? ? ? long timestamp = timeGen();

? ? ? ? ? ? while (timestamp <= lastTimestamp)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? timestamp = timeGen();

? ? ? ? ? ? }

? ? ? ? ? ? return timestamp;

? ? ? ? }


? ? ? ? /// <summary>

? ? ? ? /// 當前時間戳

? ? ? ? /// </summary>

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

? ? ? ? private static long timeGen()

? ? ? ? {

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

? ? ? ? }


? ? ? ? /// <summary>

? ? ? ? /// 指定時間戳

? ? ? ? /// </summary>

? ? ? ? /// <param name="Time">指定時間</param>

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

? ? ? ? private static long timeGen(int Year, int Month, int Day, int Hour, int Minute, int Second)

? ? ? ? {

? ? ? ? ? ? var UtcTime = new DateTime(Year, Month, Day, Hour, Minute, Second, DateTimeKind.Utc);

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

? ? ? ? }

? ? }

說下使用,理論上如果是單機部署,不用做任何配置工作

直接?SFID.NewID() 就可以使用。?

如果分布式的話

.Net Framework項目在Application_Start中,.Net Core項目在Configure中添加?SFID.WorkerID = 1L; 就可以 1L換成你的不同機器代號就可以,建議從配置文件讀取可以保證代碼一致性。另外不要部署ID相同的服務器,很可能會出現ID沖突。

因為就用了4位,所以最大只支持16臺機器,如果不夠用,可以去改workerIdBits的值,但是注意,這樣會壓縮ID的使用壽命,如果改為10位的話,大概可以用69年。

起始時間,我的為了保持一致使用了2010年1月1日0時。ID的使用壽命則是以這個時間點進行計算的。如果覺得不夠用修代碼中構造方法里的時間。但是注意多臺保持一致。否則不能保證ID順序遞增。?

然后大概說說修改思路。

1、關于實例化ID算法對象這個事,我覺得與其每次都初始化,然后費了半天勁保持對象生存,不如直接使用單例模式。所以方法不需要再單獨實例化。

但是這么做也是有缺點的,如果我想業務A和業務B分別使用不同ID的序列,那么多實例模式則更適合,兩個不同的業務,占位可以不一樣,并且允許出現相同ID,更節省ID,效率也相對較高。

2、關于效率不高的問題,其實是原來的代碼中計數器位過短造成的,并發達到數量達到可分配ID的峰值后,線程就會鎖死不再發放ID,直到下一毫秒。

知道問題就很好解決了,調整大計數器長度,壓縮服務器編號占位(我覺得實際生產中,很少有機會會用到1K臺機器并發)。

相關文章:?

  • 關于全局ID,雪花(snowflake)算法的說明

原文地址:http://www.cnblogs.com/kasimlz/p/7511131.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的分布式ID自增算法 Snowflake的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。