在分布式系統中,經常需要對大量的數據、消息、http請求等進行唯一標識,例如鏈路追蹤traceId、身份標識號、訂單流水號、操作記錄流水號、優惠券id等等。
這個時候數據庫自增主鍵已經不能滿足需求,需要一個能夠生成分布式ID的系統。
分布式ID的特性
- 全局唯一。不能出現重複的ID,這是最基本的要求。
- 遞增。遞增有利於關係數據庫索引性能。除了常見的連續遞增,如1001,1002,1003等等,分布式ID還存在趨勢遞增的形式,即保證下一個ID大於上一個ID但不連續。這樣的好處可以防止關鍵信息被泄露,例如toc業務中暴露給用戶的ID,可能會暴露用戶數量。(訂單id同理)
- 高可用。為多個服務提供ID服務,一旦宕機,會造成嚴重影響。
- 好接入。秉着拿來即用的設計原則,接入文檔要儘可能的簡單。
- 高性能:必須要在壓測下表現良好,如果達不到要求則在高並發環境下會導致系統癱瘓。
- 靈活多變:每個業務場景對ID的要求也各不相同,ID生成要做到靈活多變可配置,儘可能多的滿足需求。
解決方案
1. UUID
UUID是Universally Unique Identifier的縮寫,包含32個16進制數字,以連字號分為五段,形式為8-4-4-4-12包含36個字符的字符串,例如:
321dsa13-das2-d231-gfdd-213as8asd899
UUID經由一定的算法機器生成,為了保證UUID的唯一性,規範定義了包括網卡MAC地址、時間戳、名字空間、隨機或偽隨機數、時序等元素,以及從這些元素生成UUID的算法。
優點:
- 性能非常高,本地生成,沒有網絡消耗。
- 生成簡單,沒有高可用風險。
- 有利於信息安全,因為可讀性差,無規律。
缺點:
- 太長,不易於存儲。
- 無序,對MySQL索引不利,在InnoDB中,UUID的無序性可能會引起數據位置頻繁變動,嚴重影響性能。
- UUID不能標識業務含義,可讀性差。
2. 數據庫自增 ID
利用數據庫自增ID的特性來生成,如MySQL的auto_increment。其優點是數字類型,並且可以自增。當然缺點就是並發場景下的性能瓶頸。
優點:
- 簡單,利用數據庫自有功能實現。
- ID嚴格連續自增,可以實現一些對ID有特殊要求的業務。
缺點:
- 有重複發號的風險,例如MySQL數據庫主從切換的場景。
- 發號性能限制於數據庫性能。
- 強依賴數據庫,當數據庫異常時整個系統不可用。
進一步優化:
放棄主從複製的高可用架構,採用多主架構。每個主庫設置不同的起始值和相同的步長,保證了號段的隔離。
3. Redis
Redis中的incr命令,可以實現原子自增。相比較數據庫而言,Redis可支撐的並發量非常高,性能好。
但需要考慮下面兩種情況造成的數據不一致問題:
- 宕機後重啟恢復但存在未及時初始化。
- 主從切換,主從數據同步延遲。
優點:
- 簡單,自有能力。
- 高並發環境下性能好,優於數據庫。
缺點:
- 可能會重複發號。
- 需要保障Redis服務的高可用。
4. Zookeeper 實現
使用ZooKeeper作為分段節點協調工具,每台服務器首先從Zookeeper 獲取一段號碼,如[1,1000]的ID,此時Zookeeper上保存最大值 1000,每次獲取的時候都會進行判斷,如果ID <=1000,則更新本地的當前值,如果為1001,則會將Zookeeper 上的最大值更新至2000,本地緩存段更新為1001-2000,更新的時候使用分布式鎖來實現。(相當於用Zookeeper實現了基於數據庫的號段模式)
優點:
效率高。
缺點:
維護成本較高,不能同時滿足多個系統對ID的需求,不夠靈活。
5、基於數據庫的號段模式
號段模式的思想是客戶端每次從數據庫中取出一批ID供程序使用,從表中獲取本次ID值的範圍,如[1,1000],然後客戶端將申請的號段[1,1000]加載到內存。表結構參考如下:
CREATE TABLE id_generator (
id int(10) NOT NULL,
max_id bigint(20) NOT NULL COMMENT '當前最大id',
step int(20) NOT NULL COMMENT '號段的布長',
biz_type int(20) NOT NULL COMMENT '業務類型',
version int(20) NOT NULL COMMENT '樂觀鎖版本號',
PRIMARY KEY (`id`)
)
等這批號段ID用完,再次向數據庫申請新號段,對max_id字段做一次update操作(update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX),update成功則說明新號段獲取成功,新的號段範圍是(max_id ,max_id +step]
進一步優化:
在號段消耗一半的時候,提前預留下一段號段。將預留號段時機提前,減少阻塞發生概率。一般稱此為雙Buffer機制。不同業務可以設置不同的生成規則。
5.雪花算法
雪花算法(Snowflake)是twitter公司內部分布式項目採用的ID生成算法,開源後廣受國內大廠的好評,在該算法影響下各大公司相繼開發出各具特色的分布式生成器。
雪花算法,不依賴其它系統或數據庫,以服務的方式部署,供其它服務調用,穩定性高,生成 ID 的性能也非常高。
給每台機器分配一個唯一標識,然後通過下面的結構實現全局唯一ID:

- 1位。未使用(二進制中最高位為1的都是負數,所以這個最高位固定是0)
- 41位。毫秒級時間(41 位的長度可以使用 69 年)
- 10位。包含5位datacenterId和5位workerId(10位的長度最多支持部署1024個節點)
- 12位。最後12位是毫秒內的計數(12位的計數順序號支持每個節點每毫秒產生4096個ID序號)
由於在Java中64bit的整數是long類型,所以在Java中SnowFlake算法生成的id就是long來存儲的。
優點:
- 生成性能高。
- 整體上按照時間自增排序。
缺點:
- 強依賴機器時鐘,如果時鐘回撥,可能會導致服務異常。
- 不能同時滿足多個系統對ID的需求,不夠靈活。
- 在單機上是遞增的,但是由於涉及到分布式環境,每台機器上的時鐘不可能完全同步,會出現不是全局遞增的情況。
6.Tinyid
Tinyid是滴滴開源的分布式ID生成方案,開源地址見於參考文檔1,只提供基於號段模式來生成ID(加入了雙Buffer機制)。
7.Uidgenerator
UidGenerator是由百度技術部開發,開源地址見於參考文檔2,基於Snowflake實現的優化算法。借用未來時間和雙Buffer來解決時間回撥與生成性能等問題,同時結合MySQL進行ID分配。
8.Leaf
Leaf是美團開源的分布式ID生成方案,開源地址見於參考文檔3。提供兩種生成的ID的方式:雪花算法模式和號段模式。可通過配置文件來指定。
Leaf的雪花算法模式依賴於ZooKeeper,其workId的生成策略是基於ZooKeeper的順序ID來生成的;號段模式也是基於數據庫的號段模式+雙Buffer機制實現的。
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/252988.html
微信掃一掃
支付寶掃一掃