C#利用雪花算法實(shí)現(xiàn)生成ID工具類
寫在前面
傳說自然界中并不存在兩片完全一樣的雪花的,每一片雪花都擁有自己漂亮獨(dú)特的形狀、獨(dú)一無二;雪花算法也表示生成的ID如雪花般獨(dú)一無二,該算法源自Twitter。
雪花算法主要用于解決分布式系統(tǒng)的唯一Id生成問題,在生產(chǎn)環(huán)境中可以部署一個單獨(dú)的服務(wù)來運(yùn)行雪花算法,然后通過請求該服務(wù)獲取全局Id。
相對于UUID來說,其長度短,生成快,做數(shù)據(jù)庫主鍵時方便建立索引,所以整體效率要高很多。
代碼實(shí)現(xiàn)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class IdWorker
{
//機(jī)器ID
private static long workerId;
private static long twepoch = 687888001020L; //唯一時間,這是一個避免重復(fù)的隨機(jī)量,自行設(shè)定不要大于當(dāng)前時間戳
private static long sequence = 0L;
private static int workerIdBits = 4; //機(jī)器碼字節(jié)數(shù)。4個字節(jié)用來保存機(jī)器碼(定義為Long類型會出現(xiàn),最大偏移64位,所以左移64位沒有意義)
public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大機(jī)器ID
private static int sequenceBits = 10; //計數(shù)器字節(jié)數(shù),10個字節(jié)用來保存計數(shù)碼
private static int workerIdShift = sequenceBits; //機(jī)器碼數(shù)據(jù)左移位數(shù),就是后面計數(shù)器占用的位數(shù)
private static int timestampLeftShift = sequenceBits + workerIdBits; //時間戳左移動位數(shù)就是機(jī)器碼和計數(shù)器總字節(jié)數(shù)
public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒內(nèi)可以產(chǎn)生計數(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)算計算該微秒內(nèi)產(chǎn)生的計數(shù)是否已經(jīng)到達(dá)上限
if (IdWorker.sequence == 0)
{
//一微妙內(nèi)產(chǎn)生的ID計數(shù)已達(dá)上限,等待下一微妙
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;
}
}
/// <summary>
/// 生成雪花ID
/// </summary>
public static class SnowFlake
{
private static long _workerId = 9;
private static IdWorker _idWorker = null;
public static string NewId()
{
if (_idWorker == null)
_idWorker = new IdWorker(_workerId);
return _idWorker.nextId().ToString();
}
}調(diào)用示例
var id = SnowFlake.NewId();
MessageBox.Show(id.ToString());

注意事項
需要注意的是雪花算法嚴(yán)重依賴時間,所以當(dāng)發(fā)生服務(wù)器時鐘回?fù)艿膯栴}是會導(dǎo)致可能產(chǎn)生重復(fù)的id。當(dāng)然實(shí)際基本不會發(fā)生這種情況,生產(chǎn)環(huán)境中很少會回調(diào)服務(wù)器系統(tǒng)時間,如果實(shí)在要回?fù)軙r間也可以通過調(diào)整步長參數(shù)來解決。
到此這篇關(guān)于C#利用雪花算法實(shí)現(xiàn)生成ID工具類的文章就介紹到這了,更多相關(guān)C#雪花算法生成ID內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#多線程學(xué)習(xí)之(六)互斥對象用法實(shí)例
這篇文章主要介紹了C#多線程學(xué)習(xí)之互斥對象用法,實(shí)例分析了C#中互斥對象的使用技巧,非常具有實(shí)用價值,需要的朋友可以參考下2015-04-04
c#和avascript加解密之間的互轉(zhuǎn)代碼分享
這篇文章主要介紹了c#和Javascript間互轉(zhuǎn)的Xxtea加解密代碼,需要的朋友可以參考下2014-02-02
C#設(shè)置PDF表單不可編輯以及提取表單數(shù)據(jù)的操作
PDF表單是PDF中的可編輯區(qū)域,允許用戶填寫指定信息,當(dāng)表單填寫完成后,有時候我們可能需要將其設(shè)置為不可編輯,以保護(hù)表單內(nèi)容的完整性和可靠性,本文將給大家介紹C#設(shè)置PDF表單不可編輯以及提取表單數(shù)據(jù)的操作,需要的朋友可以參考下2024-06-06
DevExpress實(shí)現(xiàn)TreeList父子節(jié)點(diǎn)CheckState狀態(tài)同步的方法
這篇文章主要介紹了DevExpress實(shí)現(xiàn)TreeList父子節(jié)點(diǎn)CheckState狀態(tài)同步的方法,需要的朋友可以參考下2014-08-08
使用C#給PDF文檔添加注釋的實(shí)現(xiàn)代碼
本文將實(shí)例講述C#中如何使用免費(fèi)組件給PDF文檔添加文本注釋,包括自由文本注釋。自由文本注釋能允許我們自定義它的風(fēng)格和外觀,非常具有實(shí)用價值2017-01-01
C#?wpf使用DockPanel實(shí)現(xiàn)制作截屏框
做桌面客戶端的時候有時需要實(shí)現(xiàn)截屏功能,能夠在界面上框選截屏,本文就來為大家介紹一下wpf如何使用DockPanel制作截屏框吧,感興趣的可以了解下2023-09-09
.NET/C#實(shí)現(xiàn)識別用戶訪問設(shè)備的方法
這篇文章主要介紹了.NET/C#實(shí)現(xiàn)識別用戶訪問設(shè)備的方法,結(jié)合實(shí)例形式分析了C#識別用戶訪問設(shè)備的操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下2017-02-02
解析C#彩色圖像灰度化算法的實(shí)現(xiàn)代碼詳解
本篇文章是對C#中彩色圖像灰度化算法的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

