第4章唯一ID生成器——4.1 分布式唯一ID

在復雜的系統中,每個業務實體都需要使用ID做唯一標識,以方便進行數據操作。例如,每個用戶都有唯一的用戶ID,每條內容都有唯一的內容ID,甚至每條內容下的每條評論都有唯一的評論ID。

4.1.1 全局唯一與UUID

在互聯網還未普及的年代,由于用戶量少、網絡交互形式單調,互聯網產品后臺數據庫使用單體架構就可以滿足日常服務的需求。當時每個業務實體都對應數據庫中的一個數據表,每條數據都簡單地使用數據庫的自增主鍵作為唯一ID。

近年來,隨著互聯網用戶的爆發式增長,數據庫從單體架構演進到分庫分表的分布式架構,同一個業務實體的數據被分散到多個數據庫中。由于數據表之間相互獨立,在插入數據時會生成相同的自增主鍵。此時,如果還使用自增主鍵作為唯一ID,就會導致大量數據的標識相同,造成嚴重事故。我們應該保證無論一個業務實體的數據被分散到多少個數據庫中,每條數據的唯一ID都是全局的,這個全局唯一ID就是分布式唯一ID。

RFC 4122 規范中定義了通用唯一識別碼(Universally Unique Identifier, UUID),它是計算機體系中用于識別信息的一個128位標識符。UUID按照標準方法生成時,在實際應用中具有唯一性,UUID重復的概率可以忽略不計。JDK 1.5在語言層面實現了 UUID,可以輕松生成全球唯一ID:

import java.util.UUID;public class idgenerator {public static void main(String[] args) {String uuid = UUID.randomUUID().toString();System.out.printin(uuid);}
}

UUID的標準格式由32個十六進制數字組成,并通過連字符-分隔成8-4-4-4-12共 36 個字符的形式。例如,6a0d3e6f-allc-4b7d-bb35-c4c530a456b0123e4567-e89b-12d3- a456-426655440000。這種唯一ID的生成方式足夠簡單,利用本地計算即可生成全球唯一ID。不過,UUID具有一些缺點:

  • UUID字符串需要占用36字節的存儲空間,如果每條數據都攜帶UUID,那么在海量數據場景下存儲空間消耗較大。
  • 此外,UUID是無數據規律的長字符串,如果將其用作數據庫主鍵,則會導致數據在磁盤中的位置頻繁變動,嚴重影響數據庫的寫操作性能。

4.1.2 唯一ID生成器的特點

UUID僅適合數據量不大的場景,比如一個存儲集群使用UUID標識每個數據分區。真正可用于海量數據場景的唯一ID生成器,除保證ID不可重復外,還應該具有如下特點。

  • 空間占用小:作為每條數據都攜帶的字段,唯一ID不應該占用過多的存儲空間。
  • 高并發與高可用性:唯一ID生成器是大部分業務服務的重要依賴方,唯一ID的生成操作需要做到高并發無壓力,維持長期高可用性。
  • 唯一ID可用作數據庫主鍵:為了不對數據庫的寫操作造成負面影響,需要保證唯一ID對數據庫主鍵友好。

前兩點很好理解,最后一點,什么樣的唯一ID才對數據庫主鍵友好呢?我們以MySQL數據庫的InnoDB引擎為例。InnoDB使用基于磁盤的B+樹表示數據表,并以主鍵作為索引,即B+樹按照主鍵從小到大的順序排列數據。

如圖4-1所示,B+樹的節點使用默認為16KB大小的數據頁(Page)表示,其中:

image-20250321180226643

  • 節點分為葉節點和非葉節點,底層是葉節點。
  • 同層數據頁之間相互組成雙向鏈表。
  • 非葉節點僅保存N個主鍵作為指向下一層N個數據頁的索引,主鍵從小到大排列。
  • 葉節點保存實際的數據,葉節點組成的雙向鏈表上的所有數據按照主鍵從小到大的順序排列。

使用自增性質的字段作為InnoDB數據表的主鍵是一個很好的選擇,每次寫入新數據時,數據都被順序添加到對應數據頁的尾部;一個數據頁寫滿后,B+樹自動開辟一個新的數據頁。

如圖4.2所示,主鍵值為203的新數據被插入數據頁2的尾部,這樣一來,B+樹將會形成一個較為緊湊的索引結構,空間利用率較高;而且,每次插入數據時也不需要移動已有的數據,時間開銷很小。

image-20250321180609696

而如果使用非自增性質的字段(比如身份證號碼、電話號碼)作為主鍵,由于主鍵值較為隨機,新數據可能要被插入數據頁中間的某個位置。如圖4-3所示,主鍵值為15的新數據只能被插入數據1和數據30之間,數據30、50、90都需要向后移動。

image-20250321180702306

為了給新數據騰出位置,B+樹不得不將已有的數據向后移動——如果數據頁已滿,則會進行多次分頁操作。頻繁的數據移動和分頁操作使得B+樹在磁盤上產生大量的碎片,且時間開銷很大。因此,官方建議盡量使用自增性質的字段作為InnoDB數據表的主鍵

為了成為自增性質的主鍵,唯一ID生成器生成的唯一ID在數值上應該是遞增的,這樣的唯一ID對數據庫主鍵就是友好的。

占用8字節(64位)的long類型整數適合用作唯一ID,因為:

  • long類型雖然占 用的空間較小,但是可表示的ID范圍卻非常大
  • long類型整數很容易實現遞增的效果。

至此,本章的議題已經明確:設計一個可以生成遞增的long類型唯一ID的生成器

4.1.3 單調遞增與趨勢遞增

在正式開始設計唯一ID生成器之前,我們還需要解釋一下遞增。遞增可以分為單調遞增趨勢遞增,從技術實現的角度來看,它們的差異較大。

  • 單調遞增:T表示絕對時間點,如果Tn+1>TnT_{n+1} > T_nTn+1?>Tn?,則一定有F(Tn+1)>F(Tn)F(T_{n+1}) > F(T_n)F(Tn+1?)>F(Tn?)。如果唯一ID生成器生成的ID單調遞增,則說明下一次獲取到的ID一定大于上一次獲取到的ID。
  • 趨勢遞增:T表示絕對時間點,如果Tn+1>TnT_{n+1} > T_nTn+1?>Tn?,則大概率有F(Tn+1)>F(Tn)F(T_{n+1}) > F(T_n)F(Tn+1?)>F(Tn?)。雖然在一小段時間內數據有亂序的情況,但是從整體趨勢上看,數據是遞增的。

單調遞增和趨勢遞增的數據特點如圖4-4所示。

image-20250321190719896

雖然我們在4.1.2節中已經討論了唯一ID應該是遞增的,但無奈受限于全局時鐘、延遲等分布式系統問題,單調遞增的唯一ID生成器的設計方案往往會有較大的局限性,與此相比,趨勢遞增的唯一 ID生成器更受業界歡迎。接下來具體介紹這兩種遞增類型的唯一ID生成器設計方案的差別。

總結

在分布式架構中,如果還使用自增主鍵作為唯一ID,會發生什么問題呢?

數據庫從單體架構演進到分庫分表的分布式架構,同一個業務實體的數據被分散到多個數據庫中。由于數據表之間相互獨立,在插入數據時會生成相同的自增主鍵。此時,如果還使用自增主鍵作為唯一ID,就會導致大量數據的標識相同,造成嚴重事故。

什么是UUID呢?

UUID的標準格式由32個十六進制數字組成,并通過連字符-分隔成8-4-4-4-12共 36 個字符的形式。例如,6a0d3e6f-allc-4b7d-bb35-c4c530a456b0123e4567-e89b-12d3- a456-426655440000

UUID的缺點?

  • UUID字符串需要占用36字節的存儲空間,如果每條數據都攜帶UUID,那么在海量數據場景下存儲空間消耗較大。

  • UUID是無數據規律的長字符串,如果將其用作數據庫主鍵,則會導致數據在磁盤中的位置頻繁變動,嚴重影響數據庫的寫操作性能。

唯一ID生成器的特點?

  • 空間占用小:作為每條數據都攜帶的字段,唯一ID不應該占用過多的存儲空間。
  • 高并發與高可用性:唯一ID生成器是大部分業務服務的重要依賴方,唯一ID的生成操作需要做到高并發無壓力,維持長期高可用性。
  • 唯一ID可用作數據庫主鍵:為了不對數據庫的寫操作造成負面影響,需要保證唯一ID對數據庫主鍵友好。

為什么使用自增性質的字段作為InnoDB數據表的主鍵是一個很好的選擇?

  • 使用自增性質的字段,每次寫入新數據時,數據都被順序添加到對應數據頁的尾部;一個數據頁寫滿后,B+樹自動開辟一個新的數據頁。
  • 使用非自增性質的字段(比如身份證號碼、電話號碼)作為主鍵,由于主鍵值較為隨機,新數據可能要被插入數據頁中間的某個位置。為了給新數據騰出位置,B+樹不得不將已有的數據向后移動——如果數據頁已滿,則會進行多次分頁操作。頻繁的數據移動和分頁操作使得B+樹在磁盤上產生大量的碎片,且時間開銷很大。

為什么占用8字節(64位)的long類型整數適合用作唯一ID呢?

  • long類型雖然占用的空間較小,但是可表示的ID范圍卻非常大
  • long類型整數很容易實現遞增的效果。

什么是單調遞增和趨勢遞增?

  • 單調遞增:T表示絕對時間點,如果Tn+1>TnT_{n+1} > T_nTn+1?>Tn?,則一定有F(Tn+1)>F(Tn)F(T_{n+1}) > F(T_n)F(Tn+1?)>F(Tn?)。如果唯一ID生成器生成的ID單調遞增,則說明下一次獲取到的ID一定大于上一次獲取到的ID。
  • 趨勢遞增:T表示絕對時間點,如果Tn+1>TnT_{n+1} > T_nTn+1?>Tn?,則大概率有F(Tn+1)>F(Tn)F(T_{n+1}) > F(T_n)F(Tn+1?)>F(Tn?)。雖然在一小段時間內數據有亂序的情況,但是從整體趨勢上看,數據是遞增的。

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

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

相關文章

圖論水題日記

cf1805D 題意 給定一棵樹,規定dis(u,v)≥kdis(u,v) \geq kdis(u,v)≥k時(u,v)(u,v)(u,v)之間存在一條無向邊,求k(1,2,...n)k(1,2,...n)k(1,2,...n)時圖中的連通塊個數 思路 前置知識:樹上一點到其最遠的點一定是樹直徑的兩個端點之一若一個點…

自定義線程

每個程序至少有一個線程 —— 主線程 主線程是程序的起點,你可以從它開始創建新的線程來執行任務。為此,你需要創建自定義線程,編寫在線程中執行的代碼,并啟動它。 通過繼承創建自定義線程 創建新線程有兩種主要方式:繼…

2025真實面試試題分析-安卓客戶端開發

以下是對安卓客戶端開發工程師面試問題的分類整理、領域占比分析及高頻問題精選(基于??85道問題,總出現次數118次??)。按技術領域整合為??7大核心類別??,按占比排序并精選高頻問題標注優先級(1-5🌟…

算法學習筆記:29.拓撲排序——從原理到實戰,涵蓋 LeetCode 與考研 408 例題

拓撲排序(Topological Sorting)是一種針對有向無環圖(DAG)的線性排序算法,它將圖中的頂點按照一定規則排列,使得對于圖中的任意一條有向邊 u→v,頂點 u 都排在頂點 v 之前。拓撲排序在任務調度、…

利用Web3加密技術保障您的在線數據安全

在這個信息爆炸的數字化時代,保護個人和企業數據安全變得尤為重要。Web3技術以其去中心化和加密特性,為在線數據安全提供了新的解決方案。本文將探討Web3技術如何通過加密技術保障您的在線數據安全,并介紹如何有效利用這些技術。 什么是Web3技…

Vue實現el-checkbox單選并回顯選中

先說需求 我要在頁面進行checkbox單選并回顯 第一步先把基本的頁面寫好噢&#xff1a;vue代碼&#xff1a;別忘了寫change啊<el-form-item label"按鈕顏色:" prop"menuColor"><el-checkbox-group v-model"buttonColor" change"bin…

動態規劃--序列找優問題【1】

一、說明 動態規劃似乎針對問題很多&#xff0c;五花八門&#xff0c;似乎每一個問題都有一套具體算法。其實不是的&#xff0c;動態規劃只有兩類&#xff1a;1&#xff09;針對圖的路徑問題 2&#xff09;針對一個序列的問題。本篇講動態規劃針對序列的算法范例。 二、動態規劃…

獨家|百度副總裁尚國斌即將離職,此前統籌百度地圖;行業搜索及智能體業務總經理謝天轉崗IDG

百度人事再變動。作者|文昌龍編輯|楊舟據「市象」了解&#xff0c;近期&#xff0c;百度副總裁尚國斌即將離職。公開資料顯示&#xff0c;尚國斌2010年畢業于南開大學&#xff0c;2012年加入百度&#xff0c;先后在商業分析部、集團戰略辦、智能駕駛事業群工作。尚國斌同樣也在…

Qt 網絡編程進階:HTTP 客戶端實現

在 Qt 應用程序中&#xff0c;實現高性能、可靠的 HTTP 客戶端是常見需求。Qt 提供了豐富的網絡模塊&#xff0c;包括 QNetworkAccessManager、QNetworkRequest 和 QNetworkReply 等類&#xff0c;用于簡化 HTTP 通信。本文將深入探討 Qt 網絡編程中 HTTP 客戶端的進階實現&…

Python Requests-HTML庫詳解:從入門到實戰

一、庫簡介 Requests-HTML是Python中集網絡請求與HTML解析于一體的全能型庫&#xff0c;由知名開發者Kenneth Reitz團隊維護。它完美結合了Requests的易用性和Parsel的選擇器功能&#xff0c;并內置JavaScript渲染引擎&#xff0c;特別適合現代動態網頁抓取。最新版本&#xf…

基于springboot的小區車位租售管理系統

博主介紹&#xff1a;java高級開發&#xff0c;從事互聯網行業六年&#xff0c;熟悉各種主流語言&#xff0c;精通java、python、php、爬蟲、web開發&#xff0c;已經做了六年的畢業設計程序開發&#xff0c;開發過上千套畢業設計程序&#xff0c;沒有什么華麗的語言&#xff0…

Kafka 如何優雅實現 Varint 和 ZigZag 編碼

ByteUtils 是 Kafka 中一個非常基礎且核心的工具類。從包名 common.utils 就可以看出&#xff0c;它被廣泛用于 Kafka 的各個模塊中。它的主要職責是提供一套高效、底層的靜態方法&#xff0c;用于在字節緩沖區 (ByteBuffer)、字節數組 (byte[]) 以及輸入/輸出流 (InputStream/…

局域網 IP地址

很多童鞋搞不清楚局域網ip是什么? 什么是局域網 IP 地址? 局域網 IP 地址,也稱為 私有 IP 地址(Private IP Address),是用于在局域網內部標識設備的地址。這些地址不能直接在互聯網上被訪問,通常由路由器自動分配,用于設備之間的內部通信。 局域網 IP 地址的分類 根…

k8s的service、deployment、探針詳解

1.k8s組成圖2.service和deployment的流量轉發圖# Deployment 定義容器端口 apiVersion: apps/v1 kind: Deployment metadata:name: myapp spec:template:spec:containers:- name: nginximage: nginxports:- containerPort: 80 # 容器監聽 80name: http # 端口命名&…

【PostgreSQL教程】PostgreSQL中json類型與jsonb類型的區別

博主介紹:?全網粉絲23W+,CSDN博客專家、Java領域優質創作者,掘金/華為云/阿里云/InfoQ等平臺優質作者、專注于Java技術領域? 技術范圍:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大數據、物聯網、機器學習等設計與開發。 感興趣的可…

牛客刷題記錄01

除2&#xff01; 目錄 除2&#xff01; 題目描述&#xff1a; ?編輯 題目解析&#xff1a; 代碼實現&#xff1a; 數組中兩個字符串的最小距離__牛客網 題目描述&#xff1a; 題目解析&#xff1a; 代碼實現&#xff1a; 除2&#xff01; 題目描述&#xff1a; 給一個…

Docker Compose UI遠程訪問教程:結合貝銳花生殼實現內網穿透

對于很多剛接觸Docker的用戶來說&#xff0c;命令行操作總帶著一絲“勸退感”。尤其是要在Windows上部署服務、開放端口、配置參數時&#xff0c;稍有不慎就容易出錯。有沒有辦法像網頁后臺一樣&#xff0c;用圖形界面來管理Docker項目呢&#xff1f;答案是&#xff1a;有&…

HF83311_VB1/HF83311Q_VB1:高性能USB HiFi音頻解碼器固件技術解析

引言隨著高品質音頻體驗需求的不斷增長&#xff0c;音頻解碼器固件的性能和功能成為決定音頻設備品質的關鍵因素。本文將介紹一款基于XMOS XU316技術的高性能USB HiFi音頻解碼器固件——HF83311_VB1/HF83311Q_VB1&#xff0c;這是一款專為USB HiFi音頻應用設計的軟件解決方案。…

[ComfyUI] -入門1-ComfyUI 是什么?比 Stable Diffusion WebUI 強在哪?

ComfyUI 是一個開源的、節點可視化界面,用于構建與執行 Stable Diffusion 圖像生成流程。它把復雜的生成過程拆解為許多“節點”(如提示編碼、采樣器、控制網絡等),用戶通過連接節點,就能自由編排工作流 。這種設計適合開發者與進階用戶,更便于微調、多分支與復用流程。 …

[python][flask]flask接受get或者post參數

在 Flask 中&#xff0c;可以通過 request 對象來獲取客戶端通過 GET 或 POST 方法發送的參數。以下是如何在 Flask 中接收 GET 和 POST 參數的詳細說明&#xff1a;1. 接收 GET 參數GET 請求的參數通常通過 URL 的查詢字符串傳遞。例如&#xff0c;對于 URL http://example.co…