針對大家在使用上常出現的效能疑問,我給出以下3組最佳實務:
❄ 如果ID產生需求不超過5W個/s,不用修改任何設定參數
❄ 若超過5W個/s,低於50W個/s,建議修改:SeqBitLength=10
❄ 如果超過50W個/s,接近500W個/s,建議修改:SeqBitLength=12
總之,增加SeqBitLength 會讓效能更高,但產生的ID 會更長。
❄ 這是最佳化的雪花演算法(雪花漂移),它產生的ID更短、速度更快。
❄ 支援k8s 等容器環境自動擴容(自動註冊WorkerId),可在單機或分散式環境產生數位型唯一ID。
❄ 原生支援C#/Java/Go/C/Rust/Python/Node.js/PHP(C擴充)/SQL/ 等語言,並提供多執行緒安全呼叫動態函式庫(FFI)。
❄ 相容於所有雪花演算法(號段模式或經典模式,大廠或小廠),將來你可做任意的升級切換。
❄ 這是電腦史上最全面的雪花ID產生工具。 【截至2022年8月】
? 作為架構設計的你,想要解決資料庫主鍵唯一的問題,特別是在分散式系統多資料庫中。
?你希望資料表主鍵用最少的儲存空間,索引速度更快,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 ,是整數(佔用空間最多8位元組),以下是基於預設配置產生的ID:
129053495681099 (运行1年,长度:15)
387750301904971 (运行3年,长度:15)
646093214093387 (运行5年,长度:15)
1292658282840139 (运行10年,长度:16)
9007199254740992 (运行70年,达到 js Number 最大值,长度:16)
165399880288699493 (运行1000年,等同普通雪花算法运行1年,长度:18)
? 本演算法產生的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數字,何時能成長到超過long(有符號64位,8位元組)最大值。
?預設配置下,ID可用71000 年不重複。
? 支援1024 個工作節點時,ID可用4480 年不重複。
? 支援4096 個工作節點時,ID可用1120 年不重複。
❄ WorkerIdBitLength ,機器碼位長,決定WorkerId 的最大值,預設值6 ,取值範圍[1, 19],實際上有些語言採用無符號ushort (uint16) 類型接收該參數,所以最大值是16,如果是採用有符號short (int16),則最大值為15。
❄ WorkerId ,機器碼,最重要參數,無預設值,必須全域唯一(或相同DataCenterId 內唯一),必須程式設定,缺省條件(WorkerIdBitLength取預設值)時最大值63,理論最大值2^WorkerIdBitLength -1(不同實作語言可能會限定在65535 或32767,原理同WorkerIdBitLength 規則)。不同機器或不同應用實例不能相同,你可透過應用程式配置該值,也可透過呼叫外部服務來取得值。針對自動註冊WorkerId需求,本演算法提供預設實作:透過redis 自動註冊WorkerId 的動態函式庫,詳見「ToolsAutoRegisterWorkerId」。
特別提示:如果一台伺服器部署多個獨立服務,需要為每個服務指定不同的WorkerId。
❄ SeqBitLength ,序列數字長,預設值6 ,取值範圍[3, 21](建議不小於4),決定每毫秒基礎產生的ID個數。如果每秒請求數不超過5W,保持預設值6即可;如果超過5W,不超過50W,建議賦值10或更大,以此類推。規則要求: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(此時也要正確設定最小序列數)。
❄ BaseTime ,基礎時間(也稱為:基點時間、原點時間、紀元時間),有預設值(2020年),是毫秒時間戳(是整數,.NET是DatetTime型別),作用是:用生成ID時的系統時間與基礎時間的差值(毫秒數)作為產生ID的時間戳記。基礎時間一般無需設置,如果你覺得預設值太老,你可以重新設置,但要注意,這個值以後最好不變。
第二版計劃增加參數:
❄ DataCenterId ,資料中心ID(機房ID,預設0),請確保全域唯一。
❄ DataCenterIdBitLength ,資料中心ID長度(預設0)。
❄ TimestampType ,時間戳類型(0-毫秒,1-秒),預設0。
1️⃣ 用單例模式呼叫。本演算法採用單執行緒產生ID,多方呼叫會被互斥。在同一應用實例內,呼叫者使用多執行緒(或並行)方式呼叫本演算法,不會增加ID產出速度。
2️⃣ 指定唯一的WorkerId。必須由外部系統確保WorkerId 的全域唯一性,並賦值給本演算法入口參數。
3️⃣ 單機多執行個體部署時使用不同WorkerId。並非所有實作都支援跨進程的並發唯一,保險起見,在同一主機上部署多應用程式執行個體時,請確保各WorkerId 唯一。
4️⃣ 異常處理。演算法會拋出所有Exception,外部系統應catch 異常並做好應對處理,以免引發更大的系統崩潰。
5️⃣ 認真理解IdGeneratorOptions 的定義,這對整合和使用本演算法有幫助。
6️⃣ 使用雪花漂移演算法。雖然程式碼包含了傳統雪花演算法的定義,而且你可以在入口處指定(Method=2)來啟用傳統演算法,但仍建議你使用雪花漂移演算法(Method=1,預設的),畢竟它具有更好的伸縮力和更高的性能。
7️⃣ 不要修改核心演算法。本演算法內部參數較多,邏輯較為複雜,在你尚未掌握核心邏輯時,請勿修改核心程式碼且用於生產環境,除非經過大量細緻、科學的測試驗證。
8️⃣ 應用域內配置策略相同。當系統運行一段時間後,專案需要從程式指定WorkerId 轉到自動註冊WorkerId 時,請確保同一應用程式域內所有在使用實例採用一致的設定策略,這不僅針對WorkerId,也包含其他設定參數。
9️⃣ 管理好伺服器時間。雪花演算法依賴系統時間,不要手工大幅回呼作業系統時間。如果一定要調整,請記住:確保服務再次啟動時的系統時間大於最後一次關閉時的時間。 (註:世界級或網路級的時間同步或回撥,造成的系統時間小幅度變化,對本演算法沒影響)
配置變更是指系統運作一段時間後,再調整運作參數(IdGeneratorOptions 物件屬性),請注意:
? 1.首要原則是:BaseTime只能更舊(距離現在更遠),讓產生的ID值較歷史最大值更大,確保沒有時間重疊區,不產生重複ID。 [不建議在系統運作之後調整BaseTime]
?2. 任何時候增加WorkerIdBitLength 或SeqBitLength,都是允許的,但應慎用「減小」操作,因為這可能導致在未來某天產生的ID 與舊配置時相同。 [允許在系統運作之後增加任何一個xxxBitLength 值]
?3. 如果必須減小WorkerIdBitLength 或SeqBitLength 其中的一項,一定要滿足條件:新的兩個xxxBitLength 之和要大於舊值之和。 [不建議在運行之後縮小任何一個BitLength 值]
?4. 上述3條規則,並未在本演算法內做邏輯控制,使用者應在確認新配置符合要求後,再實施配置變更。
?唯一ID產生器,依賴WorkerId,當業務服務需要水平無差別複製(自動擴容)時,這就要求能自動註冊全域唯一WorkerId,然後才能生產唯一ID。
? 本演算法提供開源動態函式庫(go語言實作),能在容器k8s 等容器環境下,透過redis 自動註冊WorkerId。
? 透過redis註冊WorkerId,並非唯一方法。你也可以開發中心化的配置服務,各端點服務啟動時,透過中心服務取得唯一WorkerId。
? 當然,如果你的服務不需要自動擴容,那就不必自動註冊WorkerId,而是為它們分別設定全域唯一值。
?方法還有很多,例如:開發中心化的ID產生服務,由它為各端點服務(單一或批次)產生可用ID。
圖片連結:https://github.com/yitter/IdGenerator/blob/master/Tools/AutoRegisterWorkerId/regprocess.jpg
原始碼路徑:/Go/source/regworkerid/reghelper.go
下載連結:https://github.com/yitter/IdGenerator/releases/download/v1.3.3/workeridgo_lib_v1.3.3.zip
// 注册一个 WorkerId,会先注销所有本机已注册的记录
// address: Redis连接地址,单机模式示例: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 哨兵模式下的服务名称,示例:mymaster,非哨兵模式传入空字符串即可
// minWorkerId: WorkerId 最小值,示例:30
// maxWorkerId: WorkerId 最大值,示例:63
// lifeTimeSeconds: WorkerId缓存时长(秒,3的倍数),推荐值15
extern GoInt32 RegisterOne(char* server, char* password, GoInt32 db, char* sentinelMasterName, GoInt32 minWorkerId, GoInt32 maxWorkerId, GoInt32 lifeTimeSeconds);
// 注销本机已注册的 WorkerId
extern void UnRegister();
語言 | github |
---|---|
? C# | 查看範例 |
? Java | 查看範例 |
? Go | 查看範例 |
? Rust | 查看範例 |
? Python | 查看範例 |
? C | 查看範例 |
? C (PHP擴展) | 查看範例 |
? Delphi (Pascal) | 查看範例 |
? JavaScript | 查看範例 |
?TypeScript | 查看範例 |
? V | 查看範例 |
? D | 查看範例 |
開源位址:https://github.com/yitter/IdGenerator
QQ群:646049993