C#利用雪花算法實(shí)現(xiàn)生成ID工具類
寫(xiě)在前面
傳說(shuō)自然界中并不存在兩片完全一樣的雪花的,每一片雪花都擁有自己漂亮獨(dú)特的形狀、獨(dú)一無(wú)二;雪花算法也表示生成的ID如雪花般獨(dú)一無(wú)二,該算法源自Twitter。
雪花算法主要用于解決分布式系統(tǒng)的唯一Id生成問(wèn)題,在生產(chǎn)環(huán)境中可以部署一個(gè)單獨(dú)的服務(wù)來(lái)運(yùn)行雪花算法,然后通過(guò)請(qǐng)求該服務(wù)獲取全局Id。
相對(duì)于UUID來(lái)說(shuō),其長(zhǎng)度短,生成快,做數(shù)據(jù)庫(kù)主鍵時(shí)方便建立索引,所以整體效率要高很多。
代碼實(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; //唯一時(shí)間,這是一個(gè)避免重復(fù)的隨機(jī)量,自行設(shè)定不要大于當(dāng)前時(shí)間戳 private static long sequence = 0L; private static int workerIdBits = 4; //機(jī)器碼字節(jié)數(shù)。4個(gè)字節(jié)用來(lái)保存機(jī)器碼(定義為L(zhǎng)ong類型會(huì)出現(xiàn),最大偏移64位,所以左移64位沒(méi)有意義) public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大機(jī)器ID private static int sequenceBits = 10; //計(jì)數(shù)器字節(jié)數(shù),10個(gè)字節(jié)用來(lái)保存計(jì)數(shù)碼 private static int workerIdShift = sequenceBits; //機(jī)器碼數(shù)據(jù)左移位數(shù),就是后面計(jì)數(shù)器占用的位數(shù) private static int timestampLeftShift = sequenceBits + workerIdBits; //時(shí)間戳左移動(dòng)位數(shù)就是機(jī)器碼和計(jì)數(shù)器總字節(jié)數(shù) public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒內(nèi)可以產(chǎn)生計(jì)數(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)算計(jì)算該微秒內(nèi)產(chǎn)生的計(jì)數(shù)是否已經(jīng)到達(dá)上限 if (IdWorker.sequence == 0) { //一微妙內(nèi)產(chǎn)生的ID計(jì)數(shù)已達(dá)上限,等待下一微妙 timestamp = tillNextMillis(this.lastTimestamp); } } else { //不同微秒生成ID IdWorker.sequence = 0; //計(jì)數(shù)清0 } if (timestamp < lastTimestamp) { //如果當(dāng)前時(shí)間戳比上一次生成ID時(shí)時(shí)間戳還小,拋出異常,因?yàn)椴荒鼙WC現(xiàn)在生成的ID之前沒(méi)有生成過(guò) throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", this.lastTimestamp - timestamp)); } this.lastTimestamp = timestamp; //把當(dāng)前時(shí)間戳保存為最后生成ID的時(shí)間戳 long nextId = (timestamp - twepoch << timestampLeftShift) | IdWorker.workerId << IdWorker.workerIdShift | IdWorker.sequence; return nextId; } } /// <summary> /// 獲取下一微秒時(shí)間戳 /// </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)前時(shí)間戳 /// </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());
注意事項(xiàng)
需要注意的是雪花算法嚴(yán)重依賴時(shí)間,所以當(dāng)發(fā)生服務(wù)器時(shí)鐘回?fù)艿膯?wèn)題是會(huì)導(dǎo)致可能產(chǎn)生重復(fù)的id。當(dāng)然實(shí)際基本不會(huì)發(fā)生這種情況,生產(chǎn)環(huán)境中很少會(huì)回調(diào)服務(wù)器系統(tǒng)時(shí)間,如果實(shí)在要回?fù)軙r(shí)間也可以通過(guò)調(diào)整步長(zhǎng)參數(shù)來(lái)解決。
到此這篇關(guān)于C#利用雪花算法實(shí)現(xiàn)生成ID工具類的文章就介紹到這了,更多相關(guān)C#雪花算法生成ID內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#中使用@聲明變量示例(逐字標(biāo)識(shí)符)
這篇文章主要介紹了C#中使用@聲明變量示例(逐字標(biāo)識(shí)符)在C#中,@符號(hào)不僅可以加在字符串常量之前,使字符串不作轉(zhuǎn)義之用,還可以加在變量名之前,使變量名與關(guān)鍵字不沖突,這種用法稱為“逐字標(biāo)識(shí)符”,需要的朋友可以參考下2015-06-06Unity實(shí)現(xiàn)物體跟隨鼠標(biāo)移動(dòng)
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)物體跟隨鼠標(biāo)移動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01C#實(shí)現(xiàn)發(fā)送簡(jiǎn)單HTTP請(qǐng)求的方法
這篇文章主要介紹了C#實(shí)現(xiàn)發(fā)送簡(jiǎn)單HTTP請(qǐng)求的方法,涉及C#操作http的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04WPF實(shí)現(xiàn)繪制餅狀統(tǒng)計(jì)圖的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用WPF實(shí)現(xiàn)繪制簡(jiǎn)單的餅狀統(tǒng)計(jì)圖,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-10-10C#實(shí)現(xiàn)主窗體最小化后出現(xiàn)懸浮框及雙擊懸浮框恢復(fù)原窗體的方法
這篇文章主要介紹了C#實(shí)現(xiàn)主窗體最小化后出現(xiàn)懸浮框及雙擊懸浮框恢復(fù)原窗體的方法,涉及C#窗體及鼠標(biāo)事件響應(yīng)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08利用Aspose.Cells和Excel模板導(dǎo)出統(tǒng)計(jì)數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了利用Aspose.Cells和Excel模板導(dǎo)出復(fù)雜的統(tǒng)計(jì)數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12C#實(shí)現(xiàn)字符串首字母大寫(xiě)的方法示例
這篇文章主要給大家介紹了關(guān)于利用C#實(shí)現(xiàn)字符串首字母大寫(xiě)的相關(guān)資料,這是在最近工作中遇到的一個(gè)需求,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01