Menu
快讀
  • 旅遊
  • 生活
    • 美食
    • 寵物
    • 養生
    • 親子
  • 娛樂
    • 動漫
  • 時尚
  • 社會
  • 探索
  • 故事
  • 科技
  • 軍事
  • 国际
快讀

分布式系統ID的生成方法之UUID、數據庫、算法、Redis、Leaf方案

2021 年 3 月 11 日 龙巅兴趣联盟

前言

一般單機或者單數據庫的項目可能規模比較小,適應的場景也比較有限,平台的訪問量和業務量都較小,業務ID的生成方式比較原始但是夠用,它並沒有給這樣的系統帶來問題和瓶頸,所以這種情況下我們並沒有對此給予太多的關注。但是對于大廠的那種大規模複雜業務、分布式高並發的應用場景,顯然這種ID的生成方式不會像小項目一樣僅僅依靠簡單的數據自增序列來完成,而且在分布式環境下這種方式已經無法滿足業務的需求,不僅無法完成業務能力,業務ID生成的速度或者重複問題可能給系統帶來嚴重的故障。所以這一次,我們看看大廠都是怎麽分析和解決這種ID生成問題的,同時,我也將我之前使用過的方式拿出來對比,看看有什麽問題,從中能夠得到什麽啓發。

分布式ID的生成特性

在分析之前,我們先明確一下業務ID的生成特性,在此特性的基礎上,我們能夠對下面的這幾種生成方式有更加深刻的認識和感悟。

  • 全局唯一,這是基本要求,不能出現重複。
  • 數字類型,趨勢遞增,後面的ID必須比前面的大,這是從MySQL存儲引擎來考慮的,需要保證寫入數據的性能。
  • 長度短,能夠提高查詢效率,這也是從MySQL數據庫規範出發的,尤其是ID作爲主鍵時。
  • 信息安全,如果ID連續生成,勢必會泄露業務信息,甚至可能被猜出,所以需要無規則不規則。
  • 高可用低延時,ID生成快,能夠扛住高並發,延時足夠低不至于成爲業務瓶頸。

分布式ID的幾種生成辦法

下面介紹幾種我積累的分布式ID生成辦法,網絡上都能夠找得到,我通過學習積累並後期整理加上自己的感悟分享于此。雖然平時可能因爲項目規模小而用不著,但是這種提出方案的思想還是很值得學習的,尤其是像美團的Leaf方案,我感覺特別的酷。

目錄:

基于UUID

基于數據庫主鍵自增

基于數據庫多實例主鍵自增

基于類Snowflake算法

基于Redis生成辦法

基于美團的Leaf方案(ID段、雙Buffer、動態調整Step)

基于UUID

這是很容易想到的方案,畢竟UUID全球唯一的特性深入人心,但是,但凡熟悉MySQL數據庫特性的人,應該不會用此來作爲業務ID,它不可讀而且過于長,在此不是好主意,除非你的系統足夠小而且不講究這些,那就另說了。下面我們簡要總結下使用UUID作爲業務ID的優缺點,以及這種方式適用的業務場景。

優點

  • 代碼實現足夠簡單易用。
  • 本地生成沒有性能問題。
  • 因爲具備全球唯一的特性,所以對于數據庫遷移這種情況不存在問題。

缺點

  • 每次生成的ID都是無序的,而且不是全數字,且無法保證趨勢遞增。
  • UUID生成的是字符串,字符串存儲性能差,查詢效率慢。
  • UUID長度過長,不適用于存儲,耗費數據庫性能。
  • ID無一定業務含義,可讀性差。

適用場景

  • 可以用來生成如token令牌一類的場景,足夠沒辨識度,而且無序可讀,長度足夠。
  • 可以用于無純數字要求、無序自增、無可讀性要求的場景。

基于數據庫主鍵自增

使用數據庫主鍵自增的方式算是比較常用的了,以MySQL爲例,在新建表時指定主鍵以auto_increment的方式自動增長生成,或者再指定個增長步長,這在小規模單機部署的業務系統裏面足夠使用了,使用簡單而且具備一定業務性,但是在分布式高並發的系統裏面,卻是不適用的,分布式系統涉及到分庫分表,跨機器甚至跨機房部署的環境下,數據庫自增的方式滿足不了業務需求,同時在高並發大量訪問的情況之下,數據庫的承受能力是有限的,我們簡單的陳列一下這種方式的優缺點。

優點

  • 實現簡單,依靠數據庫即可,成本小。
  • ID數字化,單調自增,滿足數據庫存儲和查詢性能。
  • 具有一定的業務可讀性。

缺點

  • 強依賴DB,存在單點問題,如果數據庫宕機,則業務不可用。
  • DB生成ID性能有限,單點數據庫壓力大,無法扛高並發場景。

適用場景

  • 小規模的,數據訪問量小的業務場景。
  • 無高並發場景,插入記錄可控的場景。

基于數據庫多實例主鍵自增

上面我們大致講解了數據庫主鍵自增的方式,討論的時單機部署的情況,如果要以此提高ID生成的效率,可以橫向擴展機器,平衡單點數據庫的壓力,這種方案如何實現呢?那就是在auto_increment的基礎之上,設置step增長步長,讓DB之前生成的ID趨勢遞增且不重複。

分布式系統ID的生成方法之UUID、數據庫、算法、Redis、Leaf方案

這種方案巧妙地把64位分別劃分成多段,分開表示時間戳差值、機器標識和隨機序列,先以此生成一個64位地二進制正整數,然後再轉換成十進制進行存儲。

其中,1位標識符,不使用且標記爲0;41位時間戳,用來存儲時間戳的差值;10位機器碼,可以標識1024個機器節點,如果機器分機房部署(IDC),這10位還可以拆分,比如5位表示機房ID,5位表示機器ID,這樣就有32*32種組合,一般來說是足夠了;最後的12位隨即序列,用來記錄毫秒內的計數,一個節點就能夠生成4096個ID序號。所以綜上所述,綜合計算下來,理論上Snowflake算法方案的QPS大約爲409.6w/s,性能足夠強悍了,而且這種方式,能夠確保集群中每個節點生成的ID都是不同的,且區間內遞增。

優點

  • 每秒能夠生成百萬個不同的ID,性能佳。
  • 時間戳值在高位,中間是固定的機器碼,自增的序列在地位,整個ID是趨勢遞增的。
  • 能夠根據業務場景數據庫節點布置靈活挑戰bit位劃分,靈活度高。

缺點

  • 強依賴于機器時鍾,如果時鍾回撥,會導致重複的ID生成,所以一般基于此的算法發現時鍾回撥,都會抛異常處理,阻止ID生成,這可能導致服務不可用。

適用場景

  • 雪花算法有很明顯的缺點就是時鍾依賴,如果確保機器不存在時鍾回撥情況的話,那使用這種方式生成分布式ID是可行的,當然小規模系統完全是能夠使用的。

基于Redis生成辦法

Redis的INCR命令能夠將key中存儲的數字值增一,得益于此操作的原子特性,我們能夠巧妙地使用此來做分布式ID地生成方案,還可以配合其他如時間戳值、機器標識等聯合使用。

優點

  • 有序遞增,可讀性強。
  • 能夠滿足一定性能。

缺點

  • 強依賴于Redis,可能存在單點問題。
  • 占用寬帶,而且需要考慮網絡延時等問題帶來地性能沖擊。

適用場景

  • 對性能要求不是太高,而且規模較小業務較輕的場景,而且Redis的運行情況有一定要求,注意網絡問題和單點壓力問題,如果是分布式情況,那考慮的問題就更多了,所以一幫情況下這種方式用的比較少。

Redis的方案其實可靠性有待考究,畢竟依賴于網絡,延時故障或者宕機都可能導致服務不可用,這種風險是不得不考慮在系統設計內的。

回到頂部(go to top)

基于美團的Leaf方案

從上面的幾種分布式ID方案可以看出,能夠解決一定問題,但是都有明顯缺陷,爲此,美團在數據庫的方案基礎上做了一個優化,提出了一個叫做Leaf-segment的數據庫方案。

原方案我們每次獲取ID都需要去讀取一次數據庫,這在高並發和大數據量的情況下很容易造成數據庫的壓力,那能不能一次性獲取一批ID呢,這樣就無需頻繁的造訪數據庫了。

Leaf-segment的方案就是采用每次獲取一個ID區間段的方式來解決,區間段用完之後再去數據庫獲取新的號段,這樣一來可以大大減輕數據庫的壓力,那怎麽做呢?

很簡單,我們設計一張表如下:

相關文章:

  • 開發也需了解的運維知識之Docker
  • 架構師心得:實戰電子版書籍JVM+多線程+Kafka+Redis+Nginx+MySQL
  • CentOS 7 上安裝 Node.js + PM2 + NGINX + Redis
  • 千萬級 高並發“秒殺”架構設計
  • 網易開發三年,現跳槽螞蟻花呗,4面順利通過,拿下Java崗offer
  • 面試清單(Java崗)Java+JVM+數據庫+算法+Spring+中間件+設計模式
国际

發佈留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

©2025 快讀 | 服務協議 | DMCA | 聯繫我們