分享大廠分佈式唯一ID設計方案,爲何搞的這麼複雜?

知道的越多,不知道的就越多,業餘的像一棵小草!

編輯:業餘草
來源:www.toutiao.com/i6682672464708764174

推薦:https://www.xttblog.com/?p=5017


前言

很多人看了分佈式唯一 ID 相關的文章,覺得都設計的非常複雜,大廠的分佈式唯一ID生成方案爲什麼要設計的這麼複雜?看完本篇文章,希望能夠給你解惑!

改造數據庫主鍵自增

我們都知道,利用數據庫的自增主鍵的特性,可以實現分佈式 ID;這個ID比較簡短明瞭,適合做userId,正好符合如何永不遷移數據和避免熱點? 根據服務器指標分配數據量就符合這樣的需求場景。但這個方案有嚴重的問題:

1、一旦步長定下來,不容易擴容

2、數據庫壓力山大

我們小夥伴們看看怎麼優化這個方案,先看數據庫壓力大,爲什麼壓力大?是因爲我們每次獲取ID的時候,都要去數據庫請求一次那我們可以不可以不要每次去取

思路我們可以請求數據庫得到ID的時候,可設計成獲得的ID是一個ID區間段。

我們看上圖,有張id規則表:

1、id表示爲主鍵,無業務含義。

2、biz_tag爲了表示業務,因爲整體系統中會有很多業務需要生成ID,這樣可以共用一張表維護

3、max_id表示現在整體系統中已經分配的最大ID

4、desc描述

5、update_time表示每次取的ID時間

我們再來看看整體流程

1、【用戶服務】在註冊一個用戶時,需要一個用戶ID;會請求【生成ID服務(是獨立的應用)】的接口

2、【生成ID服務】會去查詢數據庫,找到user_tag的id,現在的max_id爲0,step=1000

3、【生成ID服務】把max_id和step返回給【用戶服務】;並且把max_id更新爲max_id = max_id + step,即更新爲1000

4、【用戶服務】獲得max_id=0,step=1000;

5、 這個用戶服務可以用ID=【max_id + 1,max_id+step】區間的ID,即爲【1,1000】

6、【用戶服務】會把這個區間保存到jvm中

7、【用戶服務】需要用到ID的時候,在區間【1,1000】中依次獲取id,可採用AtomicLong中的getAndIncrement方法。

8、如果把區間的值用完了,再去請求【生產ID服務】接口,獲取到max_id爲1000,即可以用【max_id + 1,max_id+step】區間的ID,即爲【1001,2000】

這個方案就非常完美的解決了數據庫自增的問題,而且可以自行定義max_id的起點,和step步長,非常方便擴容

而且也解決了數據庫壓力的問題,因爲在一段區間內,是在jvm內存中獲取的,而不需要每次請求數據庫。即使數據庫宕機了,系統也不受影響,ID還能維持一段時間。

競爭問題

以上方案中,如果是多個用戶服務,同時獲取ID,同時去請求【ID服務】,在獲取max_id的時候會存在併發問題。

如用戶服務A,取到的max_id=1000 ;用戶服務B取到的也是max_id=1000,那就出現了問題,Id重複了。那怎麼解決?

其實方案很多,加分佈式鎖,保證同一時刻只有一個用戶服務獲取max_id。當然也可以用數據庫自身的鎖去解決。

利用事務方式加行鎖,上面的語句,在沒有執行完之前,是不允許第二個用戶服務請求過來的,第二個請求只能阻塞。

突發阻塞問題

上圖中,多個用戶服務獲取到了各自的ID區間,在高併發場景下,id用的很快,如果3個用戶服務在某一時刻都用完了,同時去請求【ID服務】。因爲上面提到的競爭問題,所有隻有一個用戶服務去操作數據庫,其他二個會被阻塞。

小夥伴就會問,有這麼巧嗎?同時id用完。我們這裏舉的是3個用戶服務,感覺概率不大;如果是100個用戶服務呢?概率是不是一下子大了。

出現的現象就是一會兒突然系統耗時變長,一會兒好了,就是這個原因導致的,怎麼去解決?

雙buffer方案

在一般的系統設計中,雙buffer會經常看到,怎麼去解決上面的問題也可以採用雙buffer方案。

在設計的時候,採用雙buffer方案,上圖的流程:

1、當前獲取ID在buffer1中,每次獲取ID在buffer1中獲取

2、當buffer1中的Id已經使用到了100,也就是達到區間的10%

3、達到了10%,先判斷buffer2中有沒有去獲取過,如果沒有就立即發起請求獲取ID線

程,此線程把獲取到的ID,設置到buffer2中。

4、如果buffer1用完了,會自動切換到buffer2

5、buffer2用到10%了,也會啓動線程再次獲取,設置到buffer1中

6、依次往返

雙buffer的方案,小夥伴們有沒有感覺很酷,這樣就達到了業務場景用的ID,都是在jvm內存中獲得的,從此不需要到數據庫中獲取了。允許數據庫宕機時間更長了。

因爲會有一個線程,會觀察什麼時候去自動獲取。兩個buffer之間自行切換使用。就解決了突發阻塞的問題。

總結

此方案是美團公司使用的分佈式ID算法,小夥伴們如果想了解更深,可以去網上搜下,我這裏應該介紹了比較詳細了。

當然此方案美團還做了一些別的優化,監控id使用頻率,自動設置步長step,從而達到對id節省使用。

此 ID 方案非常適合這樣的需求:如何永不遷移數據和避免熱點? 根據服務器指標分配數據量 。

但此 ID 存在一定的問題,就是太過連續,競爭對手可以預測,不適合訂單ID。那還有沒有別的方案。下次再介紹一下美團的另一種生成ID方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章