基於分佈式系統的7種唯一ID實現方案,值得收藏

概述

系統唯一ID是我們在設計一個系統的時候常常會遇見的問題,也常常為這個問題而糾結。生成ID的方法有很多,適應不同的場景、需求以及性能要求。所以有些比較復雜的系統會有多個ID生成的策略。

分佈式ID的特性唯一性:確保生成的ID是全網唯一的。有序遞增性:確保生成的ID是對於某個用戶或者業務是按一定的數字有序遞增的。高可用性:確保任何時候都能正確的生成ID。帶時間:ID裡面包含時間,一眼掃過去就知道哪天的交易。

下面介紹幾種分佈式ID的生成方案。

1、數據庫自增長序列或字段

最常見的方式。利用數據庫,全數據庫唯一。

優點:

1)簡單,代碼方便,性能可以接受。2)數字ID天然排序,對分頁或者需要排序的結果很有幫助。

缺點:

1)不同數據庫語法和實現不同,數據庫遷移的時候或多數據庫版本支持的時候需要處理。2)在單個數據庫或讀寫分離或一主多從的情況下,隻有一個主庫可以生成。有單點故障的風險。3)在性能達不到要求的情況下,比較難於擴展。4)如果遇見多個系統需要合並或者涉及到數據遷移會相當痛苦。5)分表分庫的時候會有麻煩。

優化方案:

針對主庫單點,如果有多個Master庫,則每個Master庫設置的起始數字不一樣,步長一樣,可以是Master的個數。比如:Master1 生成的是 1,4,7,10,Master2生成的是2,5,8,11 Master3生成的是 3,6,9,12。這樣就可以有效生成集群中的唯一ID,也可以大大降低ID生成數據庫操作的負載。

2、UUID

常見的方式。可以利用數據庫也可以利用程序生成,一般來說全球唯一。

優點:

1)簡單,代碼方便。2)生成ID性能非常好,基本不會有性能問題。3)全球唯一,在遇見數據遷移,系統數據合並,或者數據庫變更等情況下,可以從容應對。

缺點:

1)沒有排序,無法保證趨勢遞增。2)UUID往往是使用字符串存儲,查詢的效率比較低。3)存儲空間比較大,如果是海量數據庫,就需要考慮存儲量的問題。4)傳輸數據量大5)不可讀。

3、批量生成ID

一次按需批量生成多個ID,每次生成都需要訪問數據庫,將數據庫修改為最大的ID值,並在內存中記錄當前值及最大值。

優點:

避免瞭每次生成ID都要訪問數據庫並帶來壓力,提高性能

缺點:

屬於本地生成策略,存在單點故障,服務重啟造成ID不連續

4、Redis生成ID

當使用數據庫來生成ID性能不夠要求的時候,可以嘗試使用Redis來生成ID。這主要依賴於Redis是單線程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實現。

可以使用Redis集群來獲取更高的吞吐量。假如一個集群中有5臺Redis。可以初始化每臺Redis的值分別是1,2,3,4,5,然後步長都是5。各個Redis生成的ID為:

A:1,6,11,16,21B:2,7,12,17,22C:3,8,13,18,23D:4,9,14,19,24E:5,10,15,20,25

這個,隨便負載到哪個機確定好,未來很難做修改。但是3-5臺服務器基本能夠滿足器上,都可以獲得不同的ID。但是步長和初始值一定需要事先需要瞭。使用Redis集群也可以方式單點故障的問題。

另外,比較適合使用Redis來生成每天從0開始的流水號。比如訂單號=日期+當日自增長號。可以每天在Redis中生成一個Key,使用INCR進行累加。

優點:

1)不依賴於數據庫,靈活方便,且性能優於數據庫。2)數字ID天然排序,對分頁或者需要排序的結果很有幫助。

缺點:

1)如果系統中沒有Redis,還需要引入新的組件,增加系統復雜度。2)需要編碼和配置的工作量比較大。

5、Twitter的snowflake算法(目前我們在使用的)

snowflake是Twitter開源的分佈式ID生成算法,結果是一個long型的ID。雪花算法將生成不高於19位的有序Long型整數,多用於分佈式環境的數據主鍵。

基於分佈式系統的7種唯一ID實現方案,值得收藏

其核心思想是:使用41bit作為毫秒數,10bit作為機器的ID(5個bit是數據中心,5個bit的機器ID),12bit作為毫秒內的流水號(意味著每個節點在每毫秒可以產生 4096 個 ID),最後還有一個符號位,永遠是0。

基於分佈式系統的7種唯一ID實現方案,值得收藏

snowflake算法可以根據自身項目的需要進行一定的修改。比如估算未來的數據中心個數,每個數據中心的機器數以及統一毫秒可以能的並發數來調整在算法中所需要的bit數。

優點:

1)不依賴於數據庫,靈活方便,且性能優於數據庫。2)ID按照時間在單機上是遞增的。

缺點:

在單機上是遞增的,但是由於涉及到分佈式環境,每臺機器上的時鐘不可能完全同步,也許有時候也會出現不是全局遞增的情況。

6、利用zookeeper生成唯一ID

zookeeper主要通過其znode數據版本來生成序列號,可以生成32位和64位的數據版本號,客戶端可以使用這個版本號來作為唯一的序列號。

很少會使用zookeeper來生成唯一ID。主要是由於需要依賴zookeeper,並且是多步調用API,如果在競爭較大的情況下,需要考慮使用分佈式鎖。因此,性能在高並發的分佈式環境下,也不甚理想。

7、 MongoDB的ObjectId

MongoDB的ObjectId和snowflake算法類似。它設計成輕量型的,不同的機器都能用全局唯一的同種方法方便地生成它。MongoDB 從一開始就設計用來作為分佈式數據庫,處理多個節點是一個核心要求。使其在分片環境中要容易生成得多。

MongoDB 中我們經常會接觸到一個自動生成的字段:”_id”,類型為ObjectId。

之前我們使用MySQL等關系型數據庫時,主鍵都是設置成自增的。但在分佈式環境下,這種方法就不可行瞭,會產生沖突。為此,mongodb采用瞭一個稱之為ObjectId的類型來做主鍵。ObjectId是一個12字節的 BSON 類型字符串。按照字節順序,一次代表:

4字節:UNIX時間戳 3字節:表示運行MongoDB的機器 2字節:表示生成此_id的進程 3字節:由一個隨機數開始的計數器生成的值

為瞭確保在同一臺機器上並發的多個進程產生的ObjectId 是唯一的,接下來的兩字節來自產生ObjectId 的進程標識符(PID)。

基於分佈式系統的7種唯一ID實現方案,值得收藏

ObjetId的12字節

前9 字節保證瞭同一秒鐘不同機器不同進程產生的ObjectId 是唯一的。後3 字節就是一個自動增加的計數器,確保相同進程同一秒產生的ObjectId 也是不一樣的。同一秒鐘最多允許每個進程擁有2563(16777216)個不同的ObjectId。

後面會分享更多devops和DBA方面的內容,感興趣的朋友可以關註下~

基於分佈式系統的7種唯一ID實現方案,值得收藏

發佈留言

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