開源一個新的雪花演算法(雪花漂移)

yitter發表於2021-03-11

IdGenerator

介紹

用一種全新的雪花漂移演算法,讓ID更短、生成速度更快。
核心在於縮短ID長度的同時,還能擁有極高瞬時併發處理量(50W/0.1s),及強大的配置能力。

需求來源

1.作為架構設計的你,想要解決資料庫主鍵唯一的問題,特別是在分散式系統多資料庫的時候。

2.你希望這個主鍵是用最少的儲存空間,索引速度更快,Select、Insert 和 Update 更迅速。

3.你要考慮在分庫分表(合庫合表)的時候,主鍵值可直接使用,並能反映業務時序。

4.如果這樣的主鍵值太長,超過前端 JS Number 型別最大值,須把 Long 型轉換為 String 型,你會覺得有點沮喪。

5.哪怕 Guid 能自增,但佔用空間大,這也不是你想要的。

6.你希望系統能執行 100 年以上。

傳統演算法問題

1.生成的ID太長。

2.瞬時併發量不夠。

3.不能解決時間回撥問題。

4.不支援後補生成前序ID。

5.依賴外部儲存系統。

新演算法特點

1.整形數字,隨時間單調遞增(不一定連續),長度更短,用50年都不會超過 js Number型別最大值。(預設配置 WorkerId 是6bit,自增數是6bit)

2.速度更快,是傳統雪花演算法的2-5倍,0.1秒可生成50萬個。(i7筆記本,預設演算法配置6bit+6bit)

3.支援時間回撥處理。比如伺服器時間回撥1秒,本演算法能自動適應生成臨界時間的唯一ID。

4.支援手工插入新ID。當業務需要在歷史時間生成新ID時,用本演算法的預留位能生成5000個每秒。

5.漂移時能外發通知事件。讓呼叫方確切知道演算法漂移記錄,Log併發呼叫量。

6.不依賴任何外部快取和資料庫。(但 WorkerId 必須由外部指定)

效能資料

(引數:10位自增序列,1000次漂移最大值)

連續請求量 5K 5W 50W
傳統雪花演算法 0.0045s 0.053s 0.556s
雪花漂移演算法 0.0015s 0.012s 0.113s

效果

1.js Number 型別最大數值:9007199254740992,本演算法在保持併發效能(5W+/0.01s)和最大64個 WorkerId(6bit)的同時,能用70年才到 js Number Max 值。

2.增加WorkerId位數到8bit(256節點)時,15年達到 js Number Max 值。

3.極致效能:500W/1s。

4.所有測試資料均基於8代低壓i7計算。

“我”是什麼

1.本演算法是一個類庫,它基於 net standard2.0 基礎庫,不依賴任何第三方元件。

2.本演算法不依賴任何外部資料系統(除了要被指定 WorkerId 之外)。

適用範圍

1.小型、中型、大型需要全域性唯一Id(不用Guid)的專案。

2.分散式專案。

3.不想將 Long 型轉 String 給前端用的專案。(若前端支援bigint,則可不轉型別)

如何處理時間回撥

1.當發生系統時間回撥時,演算法採用過去時序的預留序數生成新的ID。

2.預設每秒生成100個(速度可調整)。

3.回撥生成的ID序號,預設靠前,也可以調整為靠後。

4.允許時間回撥至本演算法預設基數(引數可調)。

能用多久

1.在預設配置下,ID可用 71000 年不重複。

2.在支援 1024 個工作節點時,ID可用 4480 年不重複。

3.在支援 4096 個工作節點時,ID可用 1120 年不重複。

4.以上所有工作節點,均擁有 50W/0.1s 瞬時處理速度。

★★整合建議★★

常規整合

1.用單例模式呼叫。外部整合方使用更多的例項並行呼叫本演算法,不會增加ID產出效能,因為本演算法採用單執行緒模式生成ID。

2.指定唯一的 WorkerId。必須由外部系統確保 WorkerId 的全域性唯一性,並賦值給本演算法入口方法。

3.異常處理。本演算法內部會丟擲所有 Exception,外部系統應該 catch 相關資訊並做好應對處理,以免引發更大的系統崩潰。

4.認真理解 IdGeneratorOptions 的定義,這對整合和使用本演算法有幫助。

5.訂閱ID非同步通知。IIdGenerator.GenIdActionAsync 是一個可以向外部系統非同步傳送ID生成訊息的事件,它包含的訊息型別有"漂移開始、漂移結束、時間回撥",具體參考 Yitter.IdGenTest 的 Program.cs 啟動程式碼。不過訂閱ID非同步通知會有細微的效能損失。

6.非同步或同步呼叫。你可在外部系統的非同步(async標記)方法中呼叫本演算法,同步呼叫同樣沒問題。

7.使用雪花漂移演算法。雖然程式碼裡包含了傳統雪花演算法的定義,並且你可以在入口處指定(Method=2)來啟用傳統演算法,但仍建議你使用雪花漂移演算法(Method=1,預設的),畢竟它具有更好的伸縮力和更高的效能。

8.輕易不要修改核心演算法。本演算法內部引數較多,邏輯較為複雜,在你尚未掌握核心邏輯時,請勿嘗試修改核心程式碼且用於生產環境,除非通過大量細緻、科學的測試驗證。

大型分散式整合

1.可擴大 WorkerIdBitLength 到最大20,支援 1,048,576 個節點,且不影響上述併發效能(50W/0.1s)。[演算法支援]

2.採用中心化 IdGenerator 叢集,生成可用 Id 列表,存入 Redis 佇列供節點消費。此時64箇中心化節點數足夠大型網際網路專案使用。[需整合方擴充套件實現]

3.以上2條二選一即可,採用方法2一般是因為不想增加最終 ID 長度,但節點數超過64個。

4.任何加大 WorkerIdBitLength 或 SeqBitLength 的設定,都可能會增加 ID 的長度。

配置變更

配置變更指是系統執行一段時間後,再變更執行引數(IdGeneratorOptions選項值),請注意:

1.最重要的一條原則是:StartTime 只能往前(比老值更小、距離現在更遠)賦值,原因是往後賦值極大可能產生相同的時間戳。[不推薦在系統執行之後調整 StartTime]

2.任何時候增加 WorkerIdBitLength 或 SeqBitLength,都是可以的,但是慎用 “減小”的操作,因為這可能導致在未來某天生成的 ID 與過去老配置時相同。[允許在系統執行之後增加任何一個 BitLength 值]

3.如果必須減小 WorkerIdBitLength 或 SeqBitLength 其中的一項,一定要滿足一個條件:新的兩個 BitLength 之和要大於 老的值之和。[不推薦在執行之後縮小任何一個 BitLength 值]

4.上述3條規則,並未在本演算法內做邏輯控制,整合方應根據上述規則做好影響評估,確認無誤後,再實施配置變更。

關於預設配置

1.預設配置能很好應對常規併發不超過1w次/1s,突發請求不超過10W次/1s的場景。

2.若要突發請求值滿足50W次/1s,可增加 SeqBitLength 至8或9。

程式碼示例

執行環境

1..NET Standard 2.0+

引用nuget包

<PackageReference Include="Yitter.IdGenerator" Version="1.0.2" />

呼叫示例

// 全域性初始化設定WorkerId,預設最大2^16-1。(初始化過程全域性只需一次,且必須最先設定)
var options = new IdGeneratorOptions(){ WorkerId = 1};
IdHelper.SetIdGenerator(options);

// 初始化以後,就可以在需要的地方呼叫方法生成ID。
var newId = IdHelper.NextId();

如果基於DI框架整合,可以參考 IdHelper 去管理 IdGenerator 物件,必須使用單例模式。

options說明

public class IdGeneratorOptions
{
    /// <summary>
    /// 雪花計算方法
    /// (1|2)
    /// </summary>
    public short Method { get; set; } = 1;

    /// <summary>
    /// 開始時間(UTC格式)
    /// 不能超過當前系統時間
    /// </summary>
    public DateTime StartTime { get; set; } = DateTime.MinValue;

    /// <summary>
    /// 機器碼
    /// 與 WorkerIdBitLength 有關係
    /// </summary>
    public ushort WorkerId { get; set; } = 0;

    /// <summary>
    /// 機器碼位長
    /// 範圍:2-21(要求:序列數位長+機器碼位長不超過22)。
    /// 建議範圍:6-12。
    /// </summary>
    public byte WorkerIdBitLength { get; set; } = 6;

    /// <summary>
    /// 序列數位長
    /// 範圍:2-21(要求:序列數位長+機器碼位長不超過22)。
    /// 建議範圍:6-14。
    /// </summary>
    public byte SeqBitLength { get; set; } = 6;

    /// <summary>
    /// 最大序列數(含)
    /// (由SeqBitLength計算的最大值)
    /// </summary>
    public int MaxSeqNumber { get; set; } = 0;

    /// <summary>
    /// 最小序列數(含)
    /// 預設11,不小於5,不大於MaxSeqNumber-2
    /// </summary>
    public ushort MinSeqNumber { get; set; } = 11;

    /// <summary>
    /// 最大漂移次數(含),
    /// 預設2000,推薦範圍500-10000(與計算能力有關)
    /// </summary>
    public int TopOverCostCount { get; set; } = 2000;

生成的ID

預設配置:

WorkerId = 6	(最多64個工作節點)
SeqBitLength = 6

ID示例(基於預設配置):

129053495681099        (本演算法執行1年)
387750301904971        (執行3年)
646093214093387        (執行5年)
1292658282840139       (執行10年)
9007199254740992       (js Number 最大值)
165399880288699493     (普通雪花演算法生成的ID)

本演算法生成的 ID 值,是 js Number 最大值的 1%-10%,是普通雪花演算法值的千分之一,而計算能力卻超過普通雪花演算法。

技術支援

開源地址:https://gitee.com/yitter/idgenerator

QQ群:646049993

即將推出Java、PHP等版本。

相關文章