IdGenerator:雪花算法中非常好用的純數(shù)字自增長ID生成器
當(dāng)前位置:點(diǎn)晴教程→知識管理交流
→『 技術(shù)文檔交流 』
:IdGenerator:雪花算法中非常好用的純數(shù)字自增長ID生成器 💎 最佳實(shí)踐針對大家在測試過程中,經(jīng)常出現(xiàn)的性能問題,以下給出3組最佳實(shí)踐配置: ❄ 如果生成ID速度不超過5W/s,不用修改任何配置參數(shù) ❄ 如果生成ID速度超過5W/s,低于50W,推薦修改:SeqBitLength=10 ❄ 如果生成ID速度超過50W/s,接近500W,推薦修改:SeqBitLength=12 總之,增加 SeqBitLength 會讓性能更高,但生成的 ID 也會更長。 💎 算法介紹❄ 這是優(yōu)化的雪花算法(雪花漂移),它生成的ID更短、速度更快。 ❄ 支持 k8s 等容器環(huán)境自動擴(kuò)容(自動注冊 WorkerId),可在單機(jī)或分布式環(huán)境生成數(shù)字型唯一ID。 ❄ 原生支持 C#/Java/Go/C/Rust/Python/Node.js/PHP(C擴(kuò)展)/SQL/ 等語言,并提供多線程安全調(diào)用動態(tài)庫(FFI)。 ❄ 兼容所有雪花算法(號段模式或經(jīng)典模式,大廠或小廠),將來你可做任意的升級切換。 ❄ 這是計(jì)算機(jī)歷史上最全面的雪花ID生成工具?!窘刂?022年8月】 需求來源💧 作為架構(gòu)設(shè)計(jì)的你,想要解決數(shù)據(jù)庫主鍵唯一的問題,特別是在分布式系統(tǒng)多數(shù)據(jù)庫中。 💧 你希望數(shù)據(jù)表主鍵用最少的存儲空間,索引速度更快,select、insert 和 update 更迅速。 💧 你要考慮在分庫分表(合庫合表)時(shí),主鍵值可直接使用,并能反映業(yè)務(wù)時(shí)序。 💧 如果這樣的主鍵值太長,超過前端 js Number 類型最大值,須把 Long 型轉(zhuǎn)換為 String 型,你會覺得有點(diǎn)沮喪。 💧 盡管 Guid 能自增,但占用空間大,索引速度慢,你不想用它。 💧 應(yīng)用實(shí)例可能超過50個(gè),每個(gè)并發(fā)請求可達(dá)10W/s。 💧 要在容器環(huán)境部署應(yīng)用,支持水平復(fù)制、自動擴(kuò)容。 💧 不想依賴 redis 的自增操作獲得連續(xù)的主鍵ID,因?yàn)檫B續(xù)的ID存在業(yè)務(wù)數(shù)據(jù)安全風(fēng)險(xiǎn)。 💧 你希望系統(tǒng)運(yùn)行 100 年以上。 傳統(tǒng)算法問題❌ 生成的ID太長。 ❌ 瞬時(shí)并發(fā)量不夠。 ❌ 不能解決時(shí)間回?fù)軉栴}。 ❌ 不支持后補(bǔ)生成前序ID。 ❌ 可能依賴外部存儲系統(tǒng)。 新算法特點(diǎn)✔ 整形數(shù)字,隨時(shí)間單調(diào)遞增(不一定連續(xù)),長度更短,用50年都不會超過 js Number類型最大值。(默認(rèn)配置) ✔ 速度更快,是傳統(tǒng)雪花算法的2-5倍,0.1秒可生成50萬個(gè)(基于8代低壓i7)。 ✔ 支持時(shí)間回?fù)芴幚怼1热绶?wù)器時(shí)間回?fù)?秒,本算法能自動適應(yīng)生成臨界時(shí)間的唯一ID。 ✔ 支持手工插入新ID。當(dāng)業(yè)務(wù)需要在歷史時(shí)間生成新ID時(shí),用本算法的預(yù)留位能生成5000個(gè)每秒。 ✔ 不依賴任何外部緩存和數(shù)據(jù)庫。(k8s環(huán)境下自動注冊 WorkerId 的動態(tài)庫依賴 redis) ✔ 基礎(chǔ)功能,開箱即用,無需配置文件、數(shù)據(jù)庫連接等。 性能數(shù)據(jù)(參數(shù):10位自增序列,1000次漂移最大值)
💍 極致性能:500W/s~3000W/s。(所有測試數(shù)據(jù)均基于8代低壓i7計(jì)算) 如何處理時(shí)間回?fù)?/h4>🔶 當(dāng)發(fā)生系統(tǒng)時(shí)間回?fù)軙r(shí),算法采用過去時(shí)序的預(yù)留序數(shù)生成新的ID。 🔶 回?fù)苌傻腎D序號,默認(rèn)靠前,也可以調(diào)整為靠后。 🔶 允許時(shí)間回?fù)苤帘舅惴A(yù)設(shè)基數(shù)(參數(shù)可調(diào))。 💎 ID組成說明
ID示例🟣 本算法生成的 ID ,是整數(shù)(占用空間最多8字節(jié)),以下是基于默認(rèn)配置生成的ID: 129053495681099 (運(yùn)行1年,長度:15) 387750301904971 (運(yùn)行3年,長度:15) 646093214093387 (運(yùn)行5年,長度:15) 1292658282840139 (運(yùn)行10年,長度:16) 9007199254740992 (運(yùn)行70年,達(dá)到 js Number 最大值,長度:16) 165399880288699493 (運(yùn)行1000年,等同普通雪花算法運(yùn)行1年,長度:18) 🟣 本算法生成的 ID 值,是 js Number 最大值的 1%-10%,是普通雪花算法值的千分之一,而生成速度卻超過普通雪花算法。 🟣 js Number 類型最大數(shù)值:9007199254740992,本算法在保持并發(fā)性能(5W+/0.01s)和最大64個(gè) WorkerId(6bit)的同時(shí),能用70年才到 js Number Max 值。 長度估算💍 每增加 1位 WorkerIdBitLength 或 SeqBitLength,生成的ID數(shù)字值將會乘以2(基礎(chǔ)長度可參考前一節(jié)“ID示例”),反之則除以2。 能用多久能用多久的解釋,是指生成的ID數(shù)字,何時(shí)能增長到超過 long(有符號64位,8字節(jié))最大值。 🔵 在默認(rèn)配置下,ID可用 71000 年不重復(fù)。 🔵 在支持 1024 個(gè)工作節(jié)點(diǎn)時(shí),ID可用 4480 年不重復(fù)。 🔵 在支持 4096 個(gè)工作節(jié)點(diǎn)時(shí),ID可用 1120 年不重復(fù)。 💎 參數(shù)設(shè)置❄ WorkerIdBitLength,機(jī)器碼位長,決定 WorkerId 的最大值,默認(rèn)值6,取值范圍 [1, 19],實(shí)際上有些語言采用 無符號 ushort (uint16) 類型接收該參數(shù),所以最大值是16,如果是采用 有符號 short (int16),則最大值為15。 ❄ WorkerId,機(jī)器碼,最重要參數(shù),無默認(rèn)值,必須 全局唯一(或相同 DataCenterId 內(nèi)唯一),必須 程序設(shè)定,缺省條件(WorkerIdBitLength取默認(rèn)值)時(shí)最大值63,理論最大值 2^WorkerIdBitLength-1(不同實(shí)現(xiàn)語言可能會限定在 65535 或 32767,原理同 WorkerIdBitLength 規(guī)則)。不同機(jī)器或不同應(yīng)用實(shí)例 不能相同,你可通過應(yīng)用程序配置該值,也可通過調(diào)用外部服務(wù)獲取值。針對自動注冊WorkerId需求,本算法提供默認(rèn)實(shí)現(xiàn):通過 redis 自動注冊 WorkerId 的動態(tài)庫,詳見“Tools\AutoRegisterWorkerId”。 特別提示:如果一臺服務(wù)器部署多個(gè)獨(dú)立服務(wù),需要為每個(gè)服務(wù)指定不同的 WorkerId。 ❄ SeqBitLength,序列數(shù)位長,默認(rèn)值6,取值范圍 [3, 21](建議不小于4),決定每毫秒基礎(chǔ)生成的ID個(gè)數(shù)。如果每秒請求數(shù)不超過5W,保持默認(rèn)值6即可;如果超過5W,不超過50W,建議賦值10或更大,以此類推。規(guī)則要求:WorkerIdBitLength + SeqBitLength 不超過 22。 ❄ MinSeqNumber,最小序列數(shù),默認(rèn)值5,取值范圍 [5, MaxSeqNumber],每毫秒的前5個(gè)序列數(shù)對應(yīng)編號0-4是保留位,其中1-4是時(shí)間回?fù)芟鄳?yīng)預(yù)留位,0是手工新值預(yù)留位。 ❄ MaxSeqNumber,最大序列數(shù),設(shè)置范圍 [MinSeqNumber, 2^SeqBitLength-1],默認(rèn)值0,真實(shí)最大序列數(shù)取最大值(2^SeqBitLength-1),不為0時(shí),取其為真實(shí)最大序列數(shù),一般無需設(shè)置,除非多機(jī)共享WorkerId分段生成ID(此時(shí)還要正確設(shè)置最小序列數(shù))。 ❄ BaseTime,基礎(chǔ)時(shí)間(也稱:基點(diǎn)時(shí)間、原點(diǎn)時(shí)間、紀(jì)元時(shí)間),有默認(rèn)值(2020年),是毫秒時(shí)間戳(是整數(shù),.NET是DatetTime類型),作用是:用生成ID時(shí)的系統(tǒng)時(shí)間與基礎(chǔ)時(shí)間的差值(毫秒數(shù))作為生成ID的時(shí)間戳?;A(chǔ)時(shí)間一般無需設(shè)置,如果覺得默認(rèn)值太老,你可以重新設(shè)置,不過要注意,這個(gè)值以后最好不變。 第二版計(jì)劃增加參數(shù): ❄ DataCenterId,數(shù)據(jù)中心ID(機(jī)房ID,默認(rèn)0),請確保全局唯一。 ❄ DataCenterIdBitLength,數(shù)據(jù)中心ID長度(默認(rèn)0)。 ❄ TimestampType,時(shí)間戳類型(0-毫秒,1-秒),默認(rèn)0。 常規(guī)集成1️⃣ 用單例模式調(diào)用。本算法采用單線程生成ID,多方調(diào)用會被互斥。在同一應(yīng)用實(shí)例內(nèi),調(diào)用者使用多線程(或并行)方式調(diào)用本算法,不會增加ID產(chǎn)出速度。 2️⃣ 指定唯一的 WorkerId。必須由外部系統(tǒng)確保 WorkerId 的全局唯一性,并賦值給本算法入口參數(shù)。 3️⃣ 單機(jī)多實(shí)例部署時(shí)使用不同 WorkerId。并非所有實(shí)現(xiàn)都支持跨進(jìn)程的并發(fā)唯一,保險(xiǎn)起見,在同一主機(jī)上部署多應(yīng)用實(shí)例時(shí),請確保各 WorkerId 唯一。 4️⃣ 異常處理。算法會拋出所有 Exception,外部系統(tǒng)應(yīng) catch 異常并做好應(yīng)對處理,以免引發(fā)更大的系統(tǒng)崩潰。 5️⃣ 認(rèn)真理解 IdGeneratorOptions 的定義,這對集成和使用本算法有幫助。 6️⃣ 使用雪花漂移算法。雖然代碼里包含了傳統(tǒng)雪花算法的定義,并且你可以在入口處指定(Method=2)來啟用傳統(tǒng)算法,但仍建議你使用雪花漂移算法(Method=1,默認(rèn)的),畢竟它具有更好的伸縮力和更高的性能。 7️⃣ 不要修改核心算法。本算法內(nèi)部參數(shù)較多,邏輯較為復(fù)雜,在你尚未掌握核心邏輯時(shí),請勿修改核心代碼且用于生產(chǎn)環(huán)境,除非通過大量細(xì)致、科學(xué)的測試驗(yàn)證。 8️⃣ 應(yīng)用域內(nèi)配置策略相同。當(dāng)系統(tǒng)運(yùn)行一段時(shí)間后,項(xiàng)目需要從程序指定 WorkerId 轉(zhuǎn)到自動注冊 WorkerId 時(shí),請確保同一應(yīng)用域內(nèi)所有在用實(shí)例采用一致的配置策略,這不僅僅針對 WorkerId,也包含其他配置參數(shù)。 9️⃣ 管理好服務(wù)器時(shí)間。雪花算法依賴系統(tǒng)時(shí)間,不要手工大幅度回調(diào)操作系統(tǒng)時(shí)間。如果一定要調(diào)整,切記:確保服務(wù)再次啟動時(shí)的系統(tǒng)時(shí)間大于最后一次關(guān)閉時(shí)的時(shí)間。(注:世界級或網(wǎng)絡(luò)級的時(shí)間同步或回?fù)?,引起的系統(tǒng)時(shí)間小幅度變化,對本算法沒影響) 配置變更配置變更是指系統(tǒng)運(yùn)行一段時(shí)間后,再調(diào)整運(yùn)行參數(shù)(IdGeneratorOptions 對象屬性),請注意: 🔴 1.首要原則是:BaseTime 只能更舊(距現(xiàn)在更遠(yuǎn)),讓生成的ID值較歷史最大值更大,確保沒有時(shí)間重疊區(qū),不產(chǎn)生重復(fù)ID。[不推薦 在系統(tǒng)運(yùn)行之后調(diào)整 BaseTime] 🔴 2.任何時(shí)候增加 WorkerIdBitLength 或 SeqBitLength,都是允許的,但應(yīng)慎用 “減小”操作,因?yàn)檫@可能導(dǎo)致在未來某天生成的 ID 與舊配置時(shí)相同。[允許在系統(tǒng)運(yùn)行之后 增加 任何一個(gè) xxxBitLength 值] 🔴 3.如果必須減小 WorkerIdBitLength 或 SeqBitLength 其中的一項(xiàng),一定要滿足條件:新的兩個(gè) xxxBitLength 之和要大于舊值之和。[不推薦 在運(yùn)行之后縮小任何一個(gè) BitLength 值] 🔴 4.上述3條規(guī)則,并未在本算法內(nèi)做邏輯控制,使用者應(yīng)在確認(rèn)新配置符合要求后,再實(shí)施配置變更。 自動注冊WorkerId🔍 唯一ID生成器,依賴WorkerId,當(dāng)業(yè)務(wù)服務(wù)需要水平無差別復(fù)制(自動擴(kuò)容)時(shí),這就要求能自動注冊全局唯一WorkerId,然后才能生產(chǎn)唯一ID。 🔍 本算法提供開源動態(tài)庫(go語言實(shí)現(xiàn)),能在容器 k8s 等容器環(huán)境下,通過 redis 自動注冊 WorkerId。 🔍 通過redis注冊WorkerId,并非唯一方法。你還可以開發(fā)中心化的配置服務(wù),各端點(diǎn)服務(wù)啟動時(shí),通過中心服務(wù)獲取唯一 WorkerId。 🔍 當(dāng)然,如果你的服務(wù)無需自動擴(kuò)容,那就不必自動注冊WorkerId,而是為它們分別設(shè)置全局唯一值。 🔍 方法還有很多,例如:開發(fā)中心化的ID生成服務(wù),由它為各端點(diǎn)服務(wù)(單個(gè)或批量)生成可用ID。 自動注冊流程圖圖片鏈接:https://github.com/yitter/IdGenerator/blob/master/Tools/AutoRegisterWorkerId/regprocess.jpg 源碼路徑:/Go/source/regworkerid/reghelper.go 動態(tài)庫下載下載鏈接:https://github.com/yitter/IdGenerator/releases/download/v1.3.3/workeridgo_lib_v1.3.3.zip 動態(tài)庫接口定義// 注冊一個(gè) WorkerId,會先注銷所有本機(jī)已注冊的記錄 // address: Redis連接地址,單機(jī)模式示例:127.0.0.1:6379,哨兵/集群模式示例:127.0.0.1:26380,127.0.0.1:26381,127.0.0.1:26382 // password: Redis連接密碼 // db: Redis指定存儲庫,示例:1 // sentinelMasterName: Redis 哨兵模式下的服務(wù)名稱,示例:mymaster,非哨兵模式傳入空字符串即可 // minWorkerId: WorkerId 最小值,示例:30 // maxWorkerId: WorkerId 最大值,示例:63 // lifeTimeSeconds: WorkerId緩存時(shí)長(秒,3的倍數(shù)),推薦值15 extern GoInt32 RegisterOne(char* server, char* password, GoInt32 db, char* sentinelMasterName, GoInt32 minWorkerId, GoInt32 maxWorkerId, GoInt32 lifeTimeSeconds); // 注銷本機(jī)已注冊的 WorkerId extern void UnRegister(); 已實(shí)現(xiàn)的語言
技術(shù)支持開源地址:https://github.com/yitter/IdGenerator QQ群:646049993 該文章在 2023/9/27 14:39:05 編輯過
|
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |