SoupCalc

Snowflake ID 產生器:深入解析 Twitter 的 63 位元分散式識別碼

什麼是 Snowflake ID?

Snowflake ID 是一個 63 位元的整數,用於在分散式系統中產生唯一識別碼,而無需中央協調點。Twitter 於 2010 年開發了此格式,以取代無法在數十臺寫入分片 MySQL 叢集的應用伺服器之間擴展的自動遞增資料庫序列。每個 Snowflake ID 是一個有號 64 位元整數,其中最高有效位元始終為零,留下 63 個有效位元劃分為四個欄位:一個 41 位元的時間戳記偏移量、一個 5 位元的資料中心識別碼、一個 5 位元的機器識別碼,以及一個 12 位元的序列號。此佈局使單一 Snowflake 服務節點每秒每毫秒能產生最多 4,096 個唯一 ID,讓系統在部署數百個工作程序時,理論上可達到每秒數百萬個 ID 的吞吐量。

Snowflake ID 是單調遞增的——在同一個時期內,每個新 ID 都比前一個更大——但它們在節點之間並非嚴格順序排列。在同一毫秒內運作的兩臺機器會產生交錯的 ID,這是一個經過取捨的設計決策:此方案以犧牲完美排序來換取絕對的運作獨立性。每個工作程序只需知道自己的資料中心和機器編號,以及當前時間,就能產生全域唯一的識別碼,無需諮詢任何對等節點或鎖定服務。

63 位元識別碼的解剖

Snowflake ID 是一個 63 位元的整數,由以下欄位組成,從最高有效位元到最低有效位元:

  • 41 位元時間戳記——自自訂時期起算的毫秒數。
  • 5 位元資料中心 ID——識別託管該工作程序的實體資料中心。
  • 5 位元機器 ID——識別該資料中心內的個別主機或程序。
  • 12 位元序列號——每個毫秒的計數器,在時鐘向前推進時重設為零。

描述此佈局的精確說法是:一個 41 位元的時間戳記、5 位元的資料中心、5 位元的機器,以及 12 位元的序列號。這些欄位總共佔用 63 個位元,最高位元的符號位元設為零,使 ID 能輕鬆放入 Java 和 C# 等語言中的有號 64 位元整數。

Twitter 時期與時間戳記元件

每個 Snowflake ID 都攜帶一個從自訂時期起算的時間戳記偏移量:1288834974657 毫秒。此值大約對應於 2010-11-04T04:42:54.657Z,選擇此時間點是為了與 Twitter 內部 Snowflake 部署的開始時間對齊。使用自訂時期而非 Unix 時期,意味著 41 位元的時間戳記欄位可以表示約 69 年的範圍才會溢位。該欄位可容納的最大時間戳記值為 2^41 減 1,即 2,199,023,255,551 毫秒——約為該時期後的 69.7 年,這將溢位日期推遲到 2080 年代。

要編碼時間戳記,從當前 Unix 時期毫秒數中減去該時期,然後將結果左移 22 位元,為三個低位欄位騰出空間。任何早於該時期的輸入時間戳記均為無效,必須被拒絕。

資料中心與機器節點識別

5 位元的資料中心欄位和 5 位元的機器欄位各自接受 0 到 31 的值,整個部署總共可產生 1,024 個唯一的節點識別碼。工作程序在啟動時被指派一個資料中心 ID 和一個機器 ID,通常透過組態檔、如 ZooKeeper 的協調服務,或命令列參數來完成。此指派在整個叢集中必須是唯一的:兩個共享相同(資料中心、機器)配對的工作程序,除非其時鐘不同步,否則會產生衝突的 ID。

這些欄位透過將資料中心值左移 17 位元、機器值左移 12 位元,然後將移位後的值以 OR 運算合併到 63 位元整數中,來插入 ID。總共 1,024 個節點對大多數實際部署來說已經足夠,但需要超過 1,024 個工作程序的環境,可以從序列號欄位或時間戳記欄位借用位元,代價是吞吐量或時期範圍的縮減。

序列計數器與時鐘邊界

12 位元的序列號欄位是 Snowflake 設計中的主力。它從 0 到 4,095 運行,對於在同一毫秒內產生的每個 ID 遞增一。當序列號達到 4,095 時,工作程序會在忙碌迴圈中自旋,直到系統時鐘推進到下一毫秒,此時序列號重設為零,產生作業繼續進行。這使得單一節點每毫秒的最大爆發速率為 4,096 個 ID。

時鐘漂移和時鐘回退是 Snowflake 演算法最顯著的兩種故障模式。如果系統時鐘向後移動——無論是由於 NTP 校正、虛擬機器遷移或手動更改——工作程序可能會產生一個時間戳記小於前一個已產生 ID 的識別碼,從而破壞唯一性保證。標準的緩解措施是當偵測到時鐘回退時,停止 ID 產生並引發錯誤,拒絕提供服務直到時鐘追上最後看到的時間戳記。無法容忍停機的部署應考慮使用專用時間服務或邏輯時鐘層。

計算範例

考慮以下輸入值:Unix 時期時間戳記為 1700000000000 毫秒,資料中心 ID 為 7,機器 ID 為 13,序列號為 4095。

編碼。 首先計算時間戳記偏移量:1700000000000 減去 Snowflake 時期 1288834974657 等於 411165025343。將此偏移量左移 22 位元得到 1724551110456246272。將資料中心值 7 左移 17 位元得到 917504。將機器值 13 左移 12 位元得到 53248。序列號不需要移位。使用位元 OR 將四個項合併,產生最終的 Snowflake ID:1724551110457221119。

解碼。 要從 ID 1724551110457221119 恢復原始欄位,遮罩最低 12 位元以提取序列號:4095。右移 12 位元並遮罩最低 5 位元以恢復機器 ID:13。右移 17 位元並遮罩最低 5 位元以恢復資料中心 ID:7。右移 22 位元以獲得時間戳記偏移量:411165025343。加上時期 1288834974657 以恢復原始 Unix 時期時間戳記:1700000000000。

往返過程是精確的,因為每個欄位都完全符合其分配的位元寬度,且在編碼過程中沒有遺失任何資訊。

準確性與限制

只要所有輸入都落在其有效範圍內,Snowflake 編碼方案就是確定性且可逆的。時間戳記偏移量必須是非負的 41 位元整數;資料中心和機器 ID 必須在 0 到 31 的範圍內;序列號必須在 0 到 4,095 的範圍內。任何超出這些範圍的輸入都會被視為無效而拒絕。

此方案不考慮閏秒或亞毫秒精度。它依賴主機系統時鐘,而該時鐘可能會漂移或被外部程序調整。在系統時鐘因大量 NTP 跳躍而向前調整的部署中,偏移量欄位會正確前進,但序列計數器在該間隙期間會處於閒置狀態,留下一段未發出的 ID。這對唯一性無害,但會在 ID 時間軸中產生不連續性。

共享相同(資料中心、機器)配對的網路分區工作程序,如果同時處於活動狀態,將會產生衝突的 ID。識別碼空間提供了 1,024 個唯一的節點槽位,超出此數量需要自訂的位元佈局變體。

資料來源

編輯紀錄

本文描述了 Twitter 2010 年參考實作中所定義的 Snowflake ID 格式。位元佈局和時期值直接取自開源的 Scala 原始碼。計算範例經過手動計算驗證,對相同數值進行編碼與解碼以確認往返正確性。關於時鐘回退和節點限制的討論,反映了分散式 ID 產生相關工程文獻中記錄的運作經驗。

本文不涵蓋第三方實作或改變位元佈局的變體——例如使用 10 位元機器 ID、不同時期,或從協調服務提取工作 ID 欄位的變體。這些變體不在原始 Snowflake 規範的範圍內。 作者:SoupCalc 編輯團隊 最後審閱日期:2026 年 8 月 11 日。