【高併發】之分散式全域性唯一 ID
一、分散式全域性唯一 ID 問題
1、為什麼需要分散式全域性唯一 ID 以及分散式 ID 的業務需求
在複雜的分散式系統中,往往需要對大量的資料和訊息進行唯一標識,如在美團點評的金融、支付、餐飲、酒店,貓眼電影等產品的系統中資料日益增長,對資料分庫分表後需要有一個唯一 ID 來標識一條資料或訊息;特別一點的如訂單、騎手、優惠券也需要有唯一 ID 做標識,此時,一個能夠生成全域性唯一 ID 的系統是非常有必要的。
2、ID 生成規則部分硬性要求
- 全域性唯一:不能出現重複的 ID 號,既然是唯一標識,那麼這就是最基本的要求
- 趨勢遞增:在 MySQL 的 innoDB 引擎中使用的是聚集索引,由於多數 RDBMS 使用 Btree 的資料結構來儲存索引資料,在主鍵的選擇上面我們應該儘量使用有序的主鍵保證寫入效能
- 單調遞增:保證下一個 ID 大於上一個 ID,例如事務版本號、IM 增量資訊、排序等特殊需求
- 資訊保安:如果 ID 是連續的,惡意使用者的扒取工作就非常容易做了,直接按照順序下載指定 URL 即可,所以在一些應用場景下,需要ID無規則,不規則讓競爭對手不好猜
- 含時間戳:這樣就能在開發中快速瞭解分散式 ID 的生成時間
3、ID 號生成系統的可用性要求
- 高可用:發一個獲取分散式 ID 的請求,伺服器就要保證 99.999% 的情況下給我建立一個唯一分散式 ID
- 低延遲:發一個獲取分散式ID的請求,伺服器就要快,極速
- 搞QPS:假如併發一口氣建立分散式 ID 請求同時殺過來,伺服器要頂得住且一下子成功建立10萬
二、一般通用方案
1、UUID
UUID(Universally Unique Identity)的標準型式包含32個16進位制數字,一連字號分為五段,形式為 8-4-4-4-12 的36個字元,比如:560e8609-e78b-68d4-716b-223344660000
其特點是:效能非常高,本地生成,沒有網路消耗。如果只考慮唯一性是沒問題的。
但是由於其隨機性的原因會導致入資料庫效能差:
- 1、無序,無法預測它的生成順序,不能成功遞增有序的數字
- 2、主鍵, ID 作為主鍵時在特定的環境會存在一些問題,比如在 DB 主鍵的場景下,MySQL 官方就有明確的建議主鍵儘量越短越好,36個字元長度的 UUID 不符合要求
- 3、索引,B+ 數索引的分裂:因為 UUID 資料是無序的,所以每一次 UUID 資料的插入都會對主鍵地域的 B+ 樹進行很大的修改,這一點很不好。插入完全無序,不但會導致一些中間節點產生分裂,也會白白創造很多不飽和的節點,這樣大大降低了資料庫插入的效能。
2、資料庫自增主鍵
在分散式裡面,資料庫的自增 ID 機制主要原理是:資料庫自增 ID 是通過 MySQL 資料庫的 replace into 實現的。
這裡的 replace into 和 insert into 功能類似,不同點在於:replace into 首先嚐試插入資料列表中,如果發現表中已經有此行資料(根據主鍵或唯一索引判斷)則先刪除,在插入,否則直接插入新資料。
但資料庫自增 ID 機制仍然不適合分散式 ID:
- 1、系統水平擴充套件比較困難:比如定義好了步長和機器臺數之後,如果要新增機器該怎麼做?假設現在只有一臺機器起始號是 1,2,3,4,5,6 (步長是1),這個時候需要擴容一臺機器一臺。可以這樣做:把第二臺機器的初始值設定得比第一臺超過很多,貌似還好,現在想象一下,如果我們線上有 100 臺機器,這個時候要擴容該怎麼辦?簡直是噩夢。所以系統水平擴充套件方案複雜難以實現。
- 2、資料庫壓力還是很大,每次獲取 ID 都得讀寫一次資料庫,非常影響效能,不符合分散式 ID 裡面的低延遲和高 QPS 的規則(在高併發下,如果都區別資料庫裡面獲取 ID,那是非常影響效能的)。
3、基於 Redis 生成全域性 ID 策略
因為 Redis 是單執行緒的,天生保證原子性,可以使用原子操作 INCR 和 INCRBY 來實現。
但要注意:在 Redis 叢集情況下,同樣和 MySQL 一樣需要設定不同的增長步長,同時 key 一定要設定有效期。
所以,可以使用 Redis 叢集來獲取更高的吞吐量,假如一個叢集中有5臺Redis,可以初始化每臺 Redis 的值分別為 1,2,3,4,5,然後步長都是5,各個 Redis 生成的 ID 為:
- A:1,6,11,16,21
- B:2,7,12,17,22
- C:3,8,13,18,23
- D:4,9,14,19,24
- E:5,10,15,20,25
雖然使用 Redis 可以解決高吞吐量的問題,但為了一個 ID 問題居然要額外維護一個 Redis 叢集,顯然,這是不切實際的。
三、雪花演算法 SnowFlake
1、SnowFlake 簡介
雪花演算法 SnowFlake 是 Twitter 產品的分散式自增 ID 演算法,具體原始碼可以參考官網:https://github.com/twitter-archive/snowflake。
Twitter 的分散式雪花演算法 SnowFlake ,經測試每秒能夠生成26萬個自增可排序的 ID
- 1、生成的 ID 能夠按照時間有序生成
- 2、演算法生成 ID 的結果是一個64位大小的整數,為一個 Long 型(轉換成字串後長度最多19個字元)
- 3、分散式系統內不會產生 ID 碰撞(有 datacenter 和 workerId 作區分)並且效率較高
SnowFlake演算法產生的ID是一個64位的整型,結構如下(每一部分用“-”符號分隔):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
1位標識部分,在java中由於long的最高位是符號位,正數是0,負數是1,一般生成的ID為正數,所以為0;
41位時間戳部分,這個是毫秒級的時間,一般實現上不會儲存當前的時間戳,而是時間戳的差值(當前時間-固定的開始時間),這樣可以使產生的ID從更小值開始;41位的時間戳可以使用69年,(1L << 41) / (1000L 60 60 24 365) = 69年;
10位節點部分,Twitter實現中使用前5位作為資料中心標識,後5位作為機器標識,可以部署1024個節點;
12位序列號部分,支援同一毫秒內同一個節點可以生成4096個ID;
SnowFlake演算法生成的ID大致上是按照時間遞增的,用在分散式系統中時,需要注意資料中心標識和機器標識必須唯一,這樣就能保證每個節點生成的ID都是唯一的。或許我們不一定都需要像上面那樣使用5位作為資料中心標識,5位作為機器標識,可以根據我們業務的需要,靈活分配節點部分,如:若不需要資料中心,完全可以使用全部10位作為機器標識;若資料中心不多,也可以只使用3位作為資料中心,7位作為機器標識。
snowflake生成的ID整體上按照時間自增排序,並且整個分散式系統內不會產生ID碰撞(由datacenter和workerId作區分),並且效率較高。
2、SnowFlake 工程落地經驗
糊塗工具包就包含了 SnowFlake 的應用:
- https://github.com/looly/hutool
- https://hutool.cn/
SpringBoot 整合雪花演算法:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>5.2.0</version>
</dependency>
相關文章
- 分散式全域性唯一ID分散式
- 框架篇:分散式全域性唯一ID框架分散式
- 面試基礎之:叢集高併發情況下如何保證分散式唯一全域性Id生成面試分散式
- 分散式全域性ID生成方案分散式
- 分散式系統全域性唯一Id(SnowFlake)雪花演算法實現分散式演算法
- 聽說:分散式ID不能全域性遞增?分散式
- 分散式唯一id生成策略分散式
- [分散式][高併發]高併發架構分散式架構
- 分散式唯一 ID 生成器分散式
- 探索 PHP 如何生成全域性唯一的 idPHP
- 分散式全域性ID生成方案彙總和對比分散式
- 分散式唯一 ID 生成器 - IDGen分散式
- Spring Boot 工程整合全域性唯一ID生成器 VestaSpring Boot
- 高併發核心技術 - 冪等性 與 分散式鎖分散式
- 分散式唯一ID的幾種生成方案分散式
- PHP 實現 Snowflake 生成分散式唯一 IDPHP分散式
- Java使用雪花演算法實現生成全域性唯一idJava演算法
- 講分散式唯一id,這篇文章很實在分散式
- 研究分散式唯一ID生成,看完這篇就夠分散式
- 分散式唯一ID解決方案-雪花演算法分散式演算法
- 冰河開源了全網首個完全開源的分散式全域性有序序列號(分散式ID)框架!!分散式框架
- 為 fastapi 新增全域性唯一請求id,用於日誌跟蹤ASTAPI
- 生成分散式唯一ID的幾種解決方案分散式
- [分散式][高併發]限流的四種策略分散式
- [分散式]高併發案例---庫存超發問題分散式
- 分散式id分散式
- jmeter介面效能測試-高併發分散式部署JMeter分散式
- [分散式]架構設計原則--高併發分散式架構
- 面試集錦(八)分散式與高併發面試分散式
- SAP UI5 確保控制元件 id 全域性唯一的實現方法UI控制元件
- 取代ZooKeeper!高併發下的分散式一致性開源元件StateSynchronizer分散式元件
- 開源專案|Go 開發的一款分散式唯一 ID 生成系統Go分散式
- 分散式ID系列(2)——UUID適合做分散式ID嗎分散式UI
- 分散式叢集與多執行緒高併發分散式執行緒
- Jmeter效能測試:高併發分散式效能測試JMeter分散式
- [分散式][高併發]熔斷策略和最佳實踐分散式
- 基於 Python 自建分散式高併發 RPC 服務Python分散式RPC
- 「分散式技術專題」併發系列三:樂觀併發控制之原型系統(分散式驗證)分散式原型