分布式ID生成方案總結整理

目錄

  • 1、為什么需要分布式ID?
  • 2、業務系統對分布式ID有什么要求?
  • 3、分布式ID生成方案
    • 3.1 UUID
    • 3.2、數據庫自增
    • 3.3、號段模式
    • 3.4、 Redis實現
    • 3.4、 雪花算法(SnowFlake)
    • 3.5、 百度Uidgenerator
    • 3.6、 美團Leaf
    • 3.7、 滴滴TinyID
1、為什么需要分布式ID?對于單體系統來說 , 主鍵ID可能會常用主鍵自動的方式進行設置,這種ID生成方法在單體項目是可行的,但是對于分布式系統,分庫分表之后,就不適應了,比如訂單表數據量太大了,分成了多個庫 , 如果還采用數據庫主鍵自增的方式,就會出現在不同庫id一致的情況,雖然是不符合業務的
分布式ID生成方案總結整理

文章插圖
2、業務系統對分布式ID有什么要求?
  • 全局唯一性:ID是作為唯一的標識,不能出現重復
  • 趨勢遞增:互聯網比較喜歡MySQL數據庫,而MySQL數據庫默認使用InnoDB存儲引擎,其使用的是聚集索引,使用有序的主鍵ID有利于保證寫入的效率
  • 單調遞增:保證下一個ID大于上一個ID,這種情況可以保證事務版本號,排序等特殊需求實現
  • 信息安全:前面說了ID要遞增,但是最好不要連續,如果ID是連續的,容易被惡意爬取數據 , 指定一系列連續的 , 所以ID遞增但是不規則是最好的
3、分布式ID生成方案
  • UUID
  • 數據庫自增
  • 號段模式
  • Redis實現
  • 雪花算法(SnowFlake)
  • 百度Uidgenerator
  • 美團Leaf
  • 滴滴TinyID
3.1 UUIDUUID (Universally Unique Identifier),通用唯一識別碼的縮寫 。UUID的標準型式包含32個16進制數字,以連字號分為五段 , 形式為8-4-4-4-12的36個字符,示例: 863e254b-ae34-4371-87da-204b71d46a7b 。UUID理論上的總數為1632=2128,約等于3.4 x 10^38 。
  • 優點
    • 性能非常高,本地生成的,不依賴于網絡
  • 缺點
    • 不易存儲 , 16 字節128位 , 36位長度的字符串
    • 信息不安全,基于MAC地址生成UUID的算法可能會造成MAC地址泄露,暴露使用者的位置
    • uuid的無序性可能會引起數據位置頻繁變動,影響性能
3.2、數據庫自增在分布式環境也可以使用mysql的自增實現分布式ID的生成,如果分庫分表了,當然不是簡單的設置好auto_increment_incrementauto_increment_offset 即可,在分布式系統中我們可以多部署幾臺機器,每臺機器設置不同的初始值 , 且步長和機器數相等 。比如有兩臺機器 。設置步長step為2,Server1的初始值為1(1,3,5,7,9 , 11…)、Server2的初始值為2(2 , 4,6,8,10…) 。這是Flickr團隊在2010年撰文介紹的一種主鍵生成策略(Ticket Servers: Distributed Unique Primary Keys on the Cheap )
假設有N臺機器,step就要設置為N,如圖進行設置:
分布式ID生成方案總結整理

文章插圖
這種方案看起來是可行的,但是如果要擴容,步長step等要重新設置 , 假如只有一臺機器,步長就是1,比如1,2,3,4,5,6,這時候如果要進行擴容,就要重新設置,機器2可以挑一個偶數的數字 , 這個數字在擴容時間內,數據庫自增要達不到這個數的,然后步長就是2,機器1要重新設置step為2,然后還是以一個奇數開始進行自增 。這個過程看起來不是很雜 , 但是,如果機器很多的話,那就要花很多時間去維護重新設置
【分布式ID生成方案總結整理】這種實現的缺陷:
  • ID沒有了單調遞增的特性,只能趨勢遞增 , 有些業務場景可能不符合
  • 數據庫壓力還是比較大,每次獲取ID都需要讀取數據庫,只能通過多臺機器提高穩定性和性能
3.3、號段模式這種模式也是現在生成分布式ID的一種方法,實現思路是會從數據庫獲取一個號段范圍 , 比如[1,1000],生成1到1000的自增ID加載到內存中,建表結構如:
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`))