雪花算法裏最好用的主鍵ID生成器
技術支持
開源地址1:https://github.com/yitter/idgenerator前端
開源地址2:https://gitee.com/yitter/idgeneratorgit
QQ羣:646049993github
爲何用雪花ID?
❄ 由於大廠也在用,推特、百度、美團、滴滴等等。redis
❄ 雪花ID是走向分佈式架構的墊腳石,若是隻會Guid和數據庫自增,怎敢說會分佈式系統架構。算法
❄ 雪花ID適合小項目、大項目、超級大項目。數據庫
💎 本算法介紹
❄ 這是優化的雪花算法(雪花漂移),它生成的ID更短、速度更快。緩存
❄ 支持 k8s 等容器環境自動擴容(自動註冊 WorkerId),可在單機或分佈式環境生成數字型惟一ID。安全
❄ 原生支持 C#/Java/Go/Rust/C 等語言,並提供 PHP 擴展及 Python、Node.js 多線程安全調用動態庫(FFI)。服務器
❄ 這是計算機歷史上最全面的雪花ID生成器,期待你來超越😀網絡
需求來源
💧 做爲架構設計的你,想要解決數據庫主鍵惟一的問題,特別是在分佈式系統多數據庫中。
💧 你但願數據表主鍵用最少的存儲空間,索引速度更快,Select、Insert 和 Update 更迅速。
💧 你要考慮在分庫分表(合庫合表)時,主鍵值可直接使用,並能反映業務時序。
💧 若是這樣的主鍵值太長,超過前端 js Number 類型最大值,須把 Long 型轉換爲 String 型,你會以爲有點沮喪。
💧 儘管 Guid 能自增,但佔用空間大,索引速度慢,你不想用它。
💧 應用實例可能超過50個,每一個併發請求可達10W/s。
💧 要在容器環境部署應用,支持水平復制、自動擴容。
💧 不想依賴 redis 的自增操做得到連續的主鍵ID,由於連續的ID存在業務數據安全風險。
💧 你但願系統運行 100 年以上。
傳統算法問題
❌ 生成的ID太長。
❌ 瞬時併發量不夠。
❌ 不能解決時間回撥問題。
❌ 不支持後補生成前序ID。
❌ 可能依賴外部存儲系統。
新算法特色
✔ 整形數字,隨時間單調遞增(不必定連續),長度更短,用50年都不會超過 js Number類型最大值。(默認配置)
✔ 速度更快,是傳統雪花算法的2-5倍,0.1秒可生成50萬個(基於8代低壓i7)。
✔ 支持時間回撥處理。好比服務器時間回撥1秒,本算法能自動適應生成臨界時間的惟一ID。
✔ 支持手工插入新ID。當業務須要在歷史時間生成新ID時,用本算法的預留位能生成5000個每秒。
✔ 不依賴任何外部緩存和數據庫。(k8s環境下自動註冊 WorkerId 的動態庫依賴 redis)
✔ 基礎功能,開箱即用,無需配置文件、數據庫鏈接等。
性能數據
(參數:10位自增序列,1000次漂移最大值)
連續請求量 | 5K | 5W | 50W |
---|---|---|---|
傳統雪花算法 | 0.0045s | 0.053s | 0.556s |
雪花漂移算法 | 0.0015s | 0.012s | 0.113s |
💍 極致性能:500W/s~3000W/s。(全部測試數據均基於8代低壓i7計算)
如何處理時間回撥
🔶 當發生系統時間回撥時,算法採用過去時序的預留序數生成新的ID。
🔶 回撥生成的ID序號,默認靠前,也能夠調整爲靠後。
🔶 容許時間回撥至本算法預設基數(參數可調)。
💎 ID組成
- 本算法生成的ID由3部分組成(沿用雪花算法定義):
- +-------------------------+--------------+----------+
- | 1.相對基礎時間的時間差 | 2.WorkerId | 3.序列數 |
- +-------------------------+--------------+----------+
- 第1部分,時間差,是生成ID時的系統時間減去 BaseTime 的總時間差(毫秒單位)。
- 第2部分,WorkerId,是區分不一樣機器或不一樣應用的惟一ID,最大值由 WorkerIdBitLength(默認6)限定。
- 第3部分,序列數,是每毫秒下的序列數,由參數中的 SeqBitLength(默認6)限定。
💎 ID示例
🟣 本算法生成的 ID ,是一串整數,最多8字節。如下是基於默認配置生成的ID:
129053495681099 (本算法運行1年) 387750301904971 (運行3年) 646093214093387 (運行5年) 1292658282840139 (運行10年) 9007199254740992 (js Number 最大值) 165399880288699493 (普通雪花算法生成的ID)
🟣 本算法生成的 ID 值,是 js Number 最大值的 1%-10%,是普通雪花算法值的千分之一,而計算能力卻超過普通雪花算法。
🟣 js Number 類型最大數值:9007199254740992,本算法在保持併發性能(5W+/0.01s)和最大64個 WorkerId(6bit)的同時,能用70年纔到 js Number Max 值。
長度估算
💍 每增長 1位 WorkerIdBitLength 或 SeqBitLength,生成的ID數字值將會乘以2(基礎長度可參考前一節「ID示例」),反之則除以2。
能用多久
🔵 在默認配置下,ID可用 71000 年不重複。
🔵 在支持 1024 個工做節點時,ID可用 4480 年不重複。
🔵 在支持 4096 個工做節點時,ID可用 1120 年不重複。
💎 參數設置
❄ WorkerIdBitLength,機器碼位長,決定 WorkerId 的最大值,默認值6,取值範圍 [1, 19],實際上有些語言採用 無符號 ushort (uint16) 類型接收該參數,因此最大值是16,若是是採用 有符號 short (int16),則最大值爲15。
❄ WorkerId,機器碼,最重要參數,無默認值,必須 全局惟一,必須 程序設定,缺省條件(WorkerIdBitLength取默認值)時最大值63,理論最大值 2^WorkerIdBitLength-1(不一樣實現語言可能會限定在 65535 或 32767,原理同 WorkerIdBitLength 規則)。不一樣機器或不一樣應用實例 不能相同,你可經過應用程序配置該值,也可經過調用外部服務獲取值。針對自動註冊WorkerId需求,本算法提供默認實現:經過 redis 自動註冊 WorkerId 的動態庫,詳見「Tools\AutoRegisterWorkerId」。
❄ SeqBitLength,序列數位長,默認值6,取值範圍 [3, 21](建議不小於4),決定每毫秒基礎生成的ID個數。規則要求:WorkerIdBitLength + SeqBitLength 不超過 22。
❄ MinSeqNumber,最小序列數,默認值5,取值範圍 [5, MaxSeqNumber],每毫秒的前5個序列數對應編號0-4是保留位,其中1-4是時間回撥相應預留位,0是手工新值預留位。
❄ MaxSeqNumber,最大序列數,設置範圍 [MinSeqNumber, 2^SeqBitLength-1],默認值0,真實最大序列數取最大值(2^SeqBitLength-1),不爲0時,取其爲真實最大序列數,通常無需設置,除非多機共享WorkerId分段生成ID(此時還要正確設置最小序列數)。
💎 常規集成
1️⃣ 用單例模式調用。外部集成方使用更多的實例並行調用本算法,不會增長ID產出效能,由於本算法採用單線程模式生成ID。
2️⃣ 指定惟一的 WorkerId。必須由外部系統確保 WorkerId 的全局惟一性,並賦值給本算法入口方法。
3️⃣ 單機多實例部署時使用不一樣 WorkerId。並不是全部實現都支持跨進程的併發惟一,保險起見,在同一主機上部署多應用實例時,請確保各 WorkerId 惟一。
4️⃣ 異常處理。算法會拋出全部 Exception,外部系統應 catch 異常並作好應對處理,以避免引起更大的系統崩潰。
5️⃣ 認真理解 IdGeneratorOptions 的定義,這對集成和使用本算法有幫助。
6️⃣ 使用雪花漂移算法。雖然代碼裏包含了傳統雪花算法的定義,而且你能夠在入口處指定(Method=2)來啓用傳統算法,但仍建議你使用雪花漂移算法(Method=1,默認的),畢竟它具備更好的伸縮力和更高的性能。
7️⃣ 不要修改核心算法。本算法內部參數較多,邏輯較爲複雜,在你還沒有掌握核心邏輯時,請勿嘗試修改核心代碼且用於生產環境,除非經過大量細緻、科學的測試驗證。
💎 配置變動
配置變動指是系統運行一段時間後,再變動運行參數(IdGeneratorOptions選項值),請注意:
🔴 1.最重要的一條原則是:BaseTime 只能往前(比老值更小、距離如今更遠)賦值,緣由是日後賦值極大可能產生相同的時間戳。[不推薦在系統運行以後調整 BaseTime]
🔴 2.任什麼時候候增長 WorkerIdBitLength 或 SeqBitLength,都是能夠的,可是慎用 「減少」的操做,由於這可能致使在將來某天生成的 ID 與過去老配置時相同。[容許在系統運行以後增長任何一個 BitLength 值]
🔴 3.若是必須減少 WorkerIdBitLength 或 SeqBitLength 其中的一項,必定要知足一個條件:新的兩個 BitLength 之和要大於 老的值之和。[不推薦在運行以後縮小任何一個 BitLength 值]
🔴 4.上述3條規則,並未在本算法內作邏輯控制,集成方應根據上述規則作好影響評估,確認無誤後,再實施配置變動。
自動註冊WorkerId
🔍 惟一ID生成器,依賴WorkerId,當業務服務須要水平無差異複製時,就要求它能自動註冊全局惟一WorkerId,而後才能根據它生產惟一ID。
🔍 本算法提供一個開源動態庫(go語言實現),能在容器 k8s(或其它容器化集羣) 環境下,經過 redis 自動註冊 WorkerId。
🔍 經過redis註冊WorkerId,並非惟一的方法。你也能夠本身開發一個配置中心服務,各個應用服務啓動時,經過配置中心獲取惟一 WorkerId。
🔍 固然,若是你的服務不須要自動擴展,你就沒必要自動註冊WorkerId,而是爲每一個應用手工設定一個惟一值。
自動註冊流程圖
圖片連接:https://gitee.com/yitter/idgenerator/blob/master/Tools/AutoRegisterWorkerId/regprocess.jpg
源碼路徑:/Go/source/regworkerid/reghelper.go
動態庫下載
下載連接:https://gitee.com/yitter/idgenerator/attach_files/662372/download/regworkerid_lib_v1.0.zip
動態庫接口定義
// 註冊一個 WorkerId,會先註銷全部本機已註冊的記錄 // ip: redis 服務器地址 // port: redis 端口 // password: redis 訪問密碼,可爲空字符串「」 // maxWorkerId: 最大 WorkerId extern GoInt32 RegisterOne(char* ip, GoInt32 port, char* password, GoInt32 maxWorkerId); // 註銷本機已註冊的 WorkerId extern void UnRegister(); // 檢查本地WorkerId是否有效(0-有效,其它-無效) extern GoInt32 Validate(GoInt32 workerId);
已實現的語言
語言 | github | gitee |
---|---|---|
🌲 C# | 查看示例 | 查看示例 |
🌲 Java | 查看示例 | 查看示例 |
🌲 Go | 查看示例 | 查看示例 |
🌲 Rust | 查看示例 | 查看示例 |
🌲 C | 查看示例 | 查看示例 |
🌲 C (PHP擴展) | 查看示例 | 查看示例 |
🌲 V | 查看示例 | 查看示例 |
🌲 D | 查看示例 | 查看示例 |
爲何不用大廠的?
❄ 首先,大廠們不但本身用雪花ID,並且還開源:百度 | 美團 | 滴滴 | 雪花ID鼻祖-推特。
❄ 然而,大廠的雪花算法分爲「經典算法」和「號段算法」兩種,其中「號段算法」依賴網絡或外部存儲系統,不適合「非大廠」,且存在沒法反應業務時序的缺點。
❄ 至於其「經典算法」,在「ID長度和生成性能」方面,未作過優化,而這正式本算法——雪花漂移算法的核心所在。