雪花算法:分布式ID生成的優雅解決方案

一、雪花算法的核心機制與設計思想

??雪花算法(Snowflake)是由Twitter開源的分布式ID生成算法,它通過巧妙的位運算設計,能夠在分布式系統中快速生成全局唯一且趨勢遞增的ID。

1. 基本結構

雪花算法生成的是一個64位(long型)的整數,這個整數被劃分為不同的部分,每個部分代表不同的含義:

  • 符號位(1位):固定為0,表示正數。
  • 時間戳部分(41位):記錄的是時間戳的差值(當前時間戳 - 開始時間戳),而非當前時間戳本身。
  • 工作機器ID部分(10位):用于標識不同的機器或節點,可進一步細分為:
    • 數據中心ID(5位):最多支持32個數據中心
    • 機器ID(5位):每個數據中心最多支持32臺機器
  • 序列號部分(12位):同一毫秒內的自增序列,最多支持4096個序列號

2. 設計思想

2.1 位運算的高效性

??雪花算法通過位運算來組合各個部分,形成最終的ID。位運算的效率非常高,能夠滿足高并發場景下的ID生成需求。基本原理是將各部分數值通過左移操作放置到對應的位置,然后通過按位或運算組合成最終的ID。

2.2 時間戳的使用

  • 選擇41位時間戳:能夠表示約69年的時間范圍((1L<<41)/(1000L360024*365) ≈ 69年)
  • 毫秒級精度:滿足大多數應用場景的時間精度需求
  • 趨勢遞增特性:由于高位是時間戳,整體ID呈現遞增趨勢,有利于數據庫索引性能

2.3 機器ID的設計

  • 分布式支持:通過10位的機器ID,最多可支持1024臺機器同時生成ID,滿足分布式系統需求
  • 靈活的劃分方式:可根據實際需求,靈活劃分數據中心ID和機器ID的位數

2.4 序列號的設計

  • 毫秒內并發:12位序列號支持同一毫秒內生成4096個不同的ID
  • 循環利用:當序列號達到最大值時,通過等待下一毫秒來重置序列號
  • 高并發支持:理論上單機每秒可生成約409.6萬個ID(4096 * 1000)

3. 核心算法流程

  1. 獲取當前時間戳
  2. 檢查時鐘回撥:如果當前時間戳小于上一次的時間戳,說明發生了時鐘回撥,需要進行特殊處理
  3. 生成序列號
    • 如果當前時間戳與上一次相同,序列號加1
    • 如果序列號超過最大值(4095),則等待下一毫秒
    • 如果當前時間戳大于上一次時間戳,序列號重置為0
  4. 更新上一次時間戳
  5. 組合ID:通過位運算將時間戳、數據中心ID、機器ID和序列號組合成最終的ID

4. 算法優勢

  1. 全局唯一性:通過時間戳、機器ID和序列號的組合,確保生成的ID在分布式環境中全局唯一
  2. 趨勢遞增:高位是時間戳,使得生成的ID整體呈現遞增趨勢,有利于數據庫索引性能
  3. 高性能:基于位運算的實現,性能極高,且不依賴外部系統
  4. 自治性:每臺機器獨立生成ID,不需要中央服務器協調,避免了單點故障
  5. 靈活性:可根據業務需求調整各部分的位數分配

5. 算法局限

  1. 強依賴機器時鐘:如果發生時鐘回撥,可能會導致ID重復或服務不可用
  2. 機器ID的配置:需要手動配置機器ID,確保在分布式環境中唯一

雪花算法通過簡潔而巧妙的設計,提供了一種高效、可靠的分布式ID生成方案,被廣泛應用于各類分布式系統中。其核心思想也啟發了許多其他分布式ID生成算法的設計和優化。

6. 雪花算法的實現機制

雪花算法的實現相對簡單,主要依靠位運算來組合各個部分,形成最終的ID。下面是算法的核心流程:

  • 首先,獲取當前時間戳。這一步通常使用系統的毫秒級時間戳函數,如Java中的System.currentTimeMillis()。

  • 接著,檢查時鐘回撥問題。如果當前時間戳小于上一次生成ID時的時間戳,說明發生了時鐘回撥,需要進行特殊處理。標準的雪花算法會在這種情況下拋出異常,但在實際應用中,通常會有更復雜的處理機制。

  • 然后,生成序列號。如果當前時間戳與上一次相同,序列號加1;如果序列號超過最大值(4095),則需要等待下一毫秒;如果當前時間戳大于上一次時間戳,序列號重置為0。

最后,通過位運算將時間戳、數據中心ID、機器ID和序列號組合成最終的ID。具體來說,將時間戳左移22位(10位機器ID + 12位序列號),將數據中心ID左移17位(5位機器ID + 12位序列號),將機器ID左移12位,然后將這些結果與序列號進行按位或運算,得到最終的ID。

下面是一個簡化的Java實現示例:

public class SnowflakeIdGenerator {// 起始時間戳,可設定為系統上線時間private final long startTimestamp = 1420041600000L; // 2015-01-01// 各部分占用的位數private final long datacenterIdBits = 5L;private final long workerIdBits = 5L;private final long sequenceBits = 12L;// 各部分最大值private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);private final long maxWorkerId = -1L ^ (-1L << workerIdBits);private final long sequenceMask = -1L ^ (-1L << sequenceBits);// 各部分向左的位移private final long workerIdShift = sequenceBits;private final long datacenterIdShift = sequenceBits + workerIdBits;private final long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;private long datacenterId;private long workerId;private long sequence = 0L;private long lastTimestamp = -1L;public SnowflakeIdGenerator(long datacenterId, long workerId) {// 參數校驗if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException("Datacenter ID can't be greater than " + maxDatacenterId + " or less than 0");}if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException("Worker ID can't be greater than " + maxWorkerId + " or less than 0");}this.datacenterId = datacenterId;this.workerId = workerId;}public synchronized long nextId() {long timestamp = System.currentTimeMillis();// 檢查時鐘回撥if (timestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards. Refusing to generate ID");}// 同一毫秒內序列號遞增if (timestamp == lastTimestamp) {sequence = (sequence + 1) & sequenceMask;// 同一毫秒內序列號用盡if (sequence == 0) {// 等待下一毫秒timestamp = waitForNextMillis(lastTimestamp);}} else {// 不同毫秒,序列號重置sequence = 0L;}lastTimestamp = timestamp;// 組合IDreturn ((timestamp - startTimestamp) << timestampShift) |(datacenterId << datacenterIdShift) |(workerId << workerIdShift) |sequence;}private long waitForNextMillis(long lastTimestamp) {long timestamp = System.currentTimeMillis();while (timestamp <= lastTimestamp) {timestamp = System.currentTimeMillis();}return timestamp;}
}

二、雪花算法使用注意事項

在實際應用雪花算法(Snowflake)生成分布式ID時,需要注意以下幾個關鍵問題和最佳實踐,以確保系統的穩定性和ID的唯一性。

1. 時鐘回撥問題

1.1 問題描述

時鐘回撥是雪花算法面臨的最大挑戰。由于算法強依賴機器時鐘,當服務器時間發生回調(例如NTP同步、人為調整等)時,可能導致生成重復的ID。

1.2 解決方案

  1. 拒絕服務策略

    • 最簡單的方法是檢測到時鐘回撥時直接拋出異常,拒絕服務
    • 適用于時鐘回撥概率低且短暫不可用影響較小的場景
  2. 等待策略

    • 當檢測到時鐘回撥時,算法等待直到當前時間追上之前記錄的最大時間
    • 適用于回撥時間較短的情況,但如果回撥時間較長,會導致服務長時間不可用
  3. 時間回撥容忍度

    • 設置一個可容忍的回撥時間閾值(如5毫秒)
    • 當回撥時間在閾值內,使用上一次的時間戳
  4. 備用位方案

    • 使用序列號中的部分位作為回撥位
    • 當發生時鐘回撥時,回撥位加1,序列號清零
    • 這種方法可以容忍一定次數的時鐘回撥
  5. 外部時間源

    • 使用外部時間源如數據庫或專門的時間服務器
    • 減少對本地時鐘的依賴

2. 機器ID分配問題

2.1 問題描述

在分布式環境中,如何確保每臺機器分配到唯一的機器ID是一個挑戰,特別是在動態擴縮容的云環境中。

2.2 解決方案

  1. 配置文件分配

    • 通過配置文件為每臺機器靜態分配ID
    • 簡單但不適合頻繁變動的環境
  2. 中心化分配

    • 使用ZooKeeper、Etcd等分布式協調服務動態分配機器ID
    • 機器啟動時向中心服務申請ID,關閉時釋放
    • 適合動態變化的環境
  3. 網絡標識分配

    • 基于機器的IP、MAC地址等網絡標識生成機器ID
    • 需要確保生成的ID在指定位數內且唯一
  4. 數據庫分配

    • 使用數據庫表記錄和分配機器ID
    • 需要考慮數據庫可用性問題

3. 時間位數與系統使用壽命

3.1 問題描述

標準雪花算法使用41位表示時間戳,可以使用約69年。但在某些特殊場景下,可能需要調整時間位數。

3.2 解決方案

  1. 自定義起始時間

    • 選擇接近系統上線時間的時間點作為起始時間
    • 最大化算法的可用時間范圍
  2. 調整位分配

    • 根據業務需求,調整時間戳、機器ID和序列號的位數分配
    • 例如,減少機器ID位數,增加時間戳位數
  3. 定期遷移策略

    • 制定長期遷移計劃,在算法接近使用壽命時進行系統升級

4. 性能與并發問題

4.1 問題描述

在高并發環境下,如何確保雪花算法的性能和ID生成的唯一性是一個挑戰。

4.2 解決方案

  1. 序列號緩沖區

    • 使用緩沖區預先生成一批ID
    • 減少實時生成的壓力
  2. 多級緩存

    • 實現多級緩存機制,減少鎖競爭
    • 提高并發性能
  3. 異步生成

    • 使用異步方式生成ID
    • 適用于對ID生成實時性要求不高的場景
  4. 批量生成

    • 支持一次請求生成多個ID
    • 減少網絡交互次數

5. 安全性考慮

5.1 問題描述

標準雪花算法生成的ID包含時間信息和機器信息,在某些場景下可能存在信息泄露風險。

5.2 解決方案

  1. ID混淆

    • 對生成的ID進行加密或混淆處理
    • 防止通過ID反推系統信息
  2. 非連續ID

    • 引入隨機因子,使生成的ID不完全連續
    • 增加ID的不可預測性
  3. 業務分離

    • 對敏感業務使用獨立的ID生成策略
    • 降低信息泄露的影響范圍

6. 分布式部署最佳實踐

  1. 合理規劃數據中心和機器ID

    • 預留足夠的擴展空間
    • 考慮未來的擴容需求
  2. 監控時鐘偏移

    • 定期檢查和記錄服務器時鐘偏移情況
    • 設置時鐘偏移告警機制
  3. 高可用部署

    • 部署多個ID生成服務實例
    • 實現負載均衡和故障轉移
  4. 定期演練

    • 模擬時鐘回撥等異常情況
    • 驗證系統的容錯能力
  5. 完善的監控和日志

    • 記錄ID生成的關鍵指標
    • 便于問題排查和性能優化

通過合理應對這些挑戰并采用最佳實踐,可以確保雪花算法在分布式系統中穩定、高效地生成全局唯一ID。

三、雪花算法的實際應用案例

雪花算法在實際應用中已經被廣泛采用,許多知名公司和開源項目都基于雪花算法實現了自己的分布式ID生成方案。

  • Twitter作為雪花算法的發明者,自然是最早的應用者。他們使用雪花算法為Twitter平臺上的每條推文、每個用戶操作生成唯一ID,支撐了海量數據的處理需求。

  • 百度的UidGenerator是基于雪花算法的開源實現,它對原始算法進行了一系列優化,包括更高效的時鐘回撥處理、RingBuffer緩沖區設計等,提高了ID生成的性能和可靠性。

  • 美團的Leaf系統提供了兩種ID生成方案:基于號段模式的分布式ID生成和基于雪花算法的分布式ID生成。其中,雪花算法部分針對時鐘回撥問題進行了特殊處理,增強了系統的穩定性。

  • 滴滴的TinyID也是一個基于雪花算法的分布式ID生成系統,它支持多種發號模式,并提供了REST API和SDK等多種接入方式,方便不同系統集成使用。

這些實際應用案例表明,雪花算法已經成為分布式ID生成領域的主流解決方案,并在實踐中不斷演進和完善。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907892.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907892.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907892.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第1章:走進Golang

第1章&#xff1a;走進Golang 一、Golang簡介 Go語言&#xff08;又稱Golang&#xff09;是由Google的Robert Griesemer、Rob Pike及Ken Thompson開發的一種開源編程語言。它誕生于2007年&#xff0c;2009年11月正式開源。Go語言的設計初衷是為了在不損失應用程序性能的情況下…

Higress項目解析(二):Proxy-Wasm Go SDK

3、Proxy-Wasm Go SDK Proxy-Wasm Go SDK 依賴于 tinygo&#xff0c;同時 Proxy - Wasm Go SDK 是基于 Proxy-Wasm ABI 規范使用 Go 編程語言擴展網絡代理&#xff08;例如 Envoy&#xff09;的 SDK&#xff0c;而 Proxy-Wasm ABI 定義了網絡代理和在網絡代理內部運行的 Wasm …

NVMe IP現狀掃盲

SSD優勢 與機械硬盤&#xff08;Hard Disk Driver, HDD&#xff09;相比&#xff0c;基于Flash的SSD具有更快的數據隨機訪問速度、更快的傳輸速率和更低的功耗優勢&#xff0c;已經被廣泛應用于各種計算領域和存儲系統。SSD最初遵循為HDD設計的現有主機接口協議&#xff0c;例…

`docker commit` 和 `docker save`區別

理解 docker commit 和 docker save 之間的區別對于正確管理 Docker 鏡像非常重要。讓我們詳細解釋一下這兩個命令的作用及其區別。 1. docker commit 作用&#xff1a; docker commit roop-builder roop:v1 命令的作用是基于一個正在運行的容器 roop-builder 創建一個新的鏡…

Linux內核體系結構簡析

1.Linux內核 1.1 Linux內核的任務 從技術層面講&#xff0c;內核是硬件和軟件之間的一個中間層&#xff0c;作用是將應用層序的請求傳遞給硬件&#xff0c;并充當底層驅動程序&#xff0c;對系統中的各種設備和組件進行尋址。從應用程序的角度講&#xff0c;應用程序與硬件沒有…

python爬蟲:Ruia的詳細使用(一個基于asyncio和aiohttp的異步爬蟲框架)

更多內容請見: 爬蟲和逆向教程-專欄介紹和目錄 文章目錄 一、Ruia概述1.1 Ruia介紹1.2 Ruia特點1.3 安裝Ruia1.4 使用案例二、基本使用2.1 Request 請求2.2 Response - 響應2.3 Item - 數據提取2.4 Field 提取數據2.5 Spider - 爬蟲類2.6 Middleware - 中間件三、高級功能3.1 …

網絡攻防技術二:密碼學分析

文章目錄 一、傳統密碼分析方法1、根據明文、密文等信息的掌握情況分類 2、從密碼分析途徑分類二、密碼旁路分析1、概念2、旁路分析方法三、現代密碼系統1、對稱密碼&#xff08;單密鑰&#xff09;2、公開密碼&#xff08;成對密鑰&#xff09; 四、典型對稱密碼&#xff08;單…

Linux --TCP協議實現簡單的網絡通信(中英翻譯)

一、什么是TCP協議 1.1 、TCP是傳輸層的協議&#xff0c;TCP需要連接&#xff0c;TCP是一種可靠性傳輸協議&#xff0c;TCP是面向字節流的傳輸協議&#xff1b; 二、TCPserver端的搭建 2.1、我們最終好實現的效果是 客戶端在任何時候都能連接到服務端&#xff0c;然后向服務…

pc端小卡片功能-原生JavaScript金融信息與節日日歷

代碼如下 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>金融信息與節日日歷</title><…

C語言——獲取變量所在地址(uint8和uint32的區別)

前言&#xff1a; 1.使用uint8 *的原因 在C語言中&#xff0c;獲取或操作一個4字節地址&#xff08;指針&#xff09;時使用uint8_t*&#xff08;即unsigned char*&#xff09;而不是uint32_t*&#xff0c;主要基于以下關鍵原因&#xff1a; 1.1. 避免違反嚴格別名規則&…

Python----目標檢測(《YOLOv3:AnIncrementalImprovement》和YOLO-V3的原理與網絡結構)

一、《YOLOv3:AnIncrementalImprovement》 1.1、基本信息 標題&#xff1a;YOLOv3: An Incremental Improvement 作者&#xff1a;Joseph Redmon, Ali Farhadi 機構&#xff1a;華盛頓大學&#xff08;University of Washington&#xff09; 發表時間&#xff1a;2018年 代…

50天50個小項目 (Vue3 + Tailwindcss V4) ? | Form Wave(表單label波動效果)

&#x1f4c5; 我們繼續 50 個小項目挑戰&#xff01;—— FormWave組件 倉庫地址&#xff1a;https://github.com/SunACong/50-vue-projects 項目預覽地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 組件目標 構建一個美觀、動態的登錄表單&#xff0…

【數據結構】--二叉樹--堆(上)

一、樹的概念和結構 概念&#xff1a; 樹是一種非線性的數據結構&#xff0c;他是由n(n>0)個有限結點組成一個具有層次關系的集合。其叫做樹&#xff0c;是因為他倒過來看就和一棵樹差不多&#xff0c;其實際上是根在上&#xff0c;樹枝在下的。 樹的特點&#xff1a; 1…

linux有效裁剪視頻的方式(基于ffmpeg,不改變分辨率,幀率,視頻質量,不需要三方軟件)

就是在Linux上使用OBS Studio錄制一個講座或者其他視頻&#xff0c;可能總有些時候會多錄制一段時間&#xff0c;但是如果使用剪映或者PR這樣的工具在導出的時候總需要煩惱導出的格式和參數&#xff0c;比如剪映就不支持mkv格式的導出&#xff0c;導出成mp4格式的視頻就會變得很…

SystemVerilog—Interface語法(一)

SystemVerilog中的接口&#xff08;interface&#xff09;是一種用于封裝多模塊間通信信號和協議的復合結構&#xff0c;可顯著提升代碼復用性和維護效率。其核心語法和功能如下&#xff1a; 一、接口的基本定義 1. 聲明語法 接口通過interface關鍵字定義&#xff0c;支持信…

android binder(四)binder驅動詳解

ref&#xff1a; Android10.0 Binder通信原理(五)-Binder驅動分析_binder: 1203:1453 ioctl 40046210 77004d93f4 return-CSDN博客 https://juejin.cn/post/7214342319347712057#heading-0 第6課第1節_Binder系統_驅動情景分析_數據結構_嗶哩嗶哩_bilibili

QT/c++航空返修數據智能分析系統

簡介 1、區分普通用戶和管理員 2、界面精美 3、功能豐富 4、使用cppjieba分詞分析數據 5、支持數據導入導出 6、echarts展示圖表 效果展示 演示鏈接 源碼獲取 int main(){ //非白嫖 printf("&#x1f4e1;:%S","joyfelic"); return 0; }

ToolsSet之:數值提取及批處理

ToolsSet是微軟商店中的一款包含數十種實用工具數百種細分功能的工具集合應用&#xff0c;應用基本功能介紹可以查看以下文章&#xff1a; Windows應用ToolsSet介紹https://blog.csdn.net/BinField/article/details/145898264 ToolsSet中Number菜單下的Numeric Batch是一個數…

Ubuntu20.04 LTS 升級Ubuntu22.04LTS 依賴錯誤 系統崩潰重裝 Ubuntu22.04 LTS

服務器系統為PowerEdge R740 BIOS Version 2.10.2 DELL EMC 1、關機 開機時連續按鍵盤F2 2、System Setup選擇第一個 System BIOS 3、System BIOS Setting 選擇 Boot Setting 4、System BIOS Setting-Boot Setting 選擇 BIOS Boot Settings 5、重啟 開啟時連續按鍵盤F11 …

(javaSE)Java數組進階:數組初始化 數組訪問 數組中的jvm 空指針異常

數組的基礎 什么是數組呢? 數組指的是一種容器,可以用來存儲同種數據類型的多個值 數組的初始化 初始化&#xff1a;就是在內存中,為數組容器開辟空間,并將數據存入容器中的過程。 數組初始化的兩種方式&#xff1a;靜態初始化&#xff0c;動態初始化 數組的靜態初始化 初始化…