多地區部署唯一自增ID的實現方法探討

這是一個偏於技術和業務之間的解決方法,解決問題的手法有很多,也就是在同一個應用在全球部署的時候,

當一個系統在全球多個地區部署的時候,由於服務的業務和當地監管機構的要求,通常在數據層面有幾種可能性:

1、數據彼此隔離,不存在相互遷移的問題。

2、以其中個別節點爲寫中心(通常1~3個),然後同步至全球其他站點,這類通常爲配置數據或部分需要共享的基礎數據,在不違背監管需求的前提下,放在一個法律上都認可的國家或地區。

 

3、每個地區或國家都存在寫入,然後彼此之間通過系統或底層數據同步工具相互同步。

 

對於方法1、2來講,實現都比較簡單,因爲寫入是比較單一的入口,然後以此爲中心,進行數據分發,異地寫入速度會受到影響,不過對於大部分業務來講,讀多寫少屬於最常見的了,所以方法可行性還是很強的。

對於方法3,各個地區本地寫入DB或某類存儲,但希望將各個地區寫入的部分數據的部分字段,相互同步或將這些數據同步到某一個數據中心當中,以便於做全局查詢或就近的全量數據查詢。

這個時候會面臨一個問題就是主鍵的解決方案,在一個單庫上面,利用數據庫的自動增長列就可以很容實現主鍵,但在這種場景下單純使用數據庫自動增長列,就會導致區域之間的ID會發生衝突,彼此之間同步數據的時候就會存在問題。當然我們的解法通常有以下幾種:

 

1、使用全球唯一碼,或其它類似UUID的字符串唯一碼

優點:不會再發生衝突,同步的問題不再關心。

缺點:主鍵從數字變字符串存儲空間會變大,插入速度會受到一定的影響(索引不是從最後位置插入,從中間插入)

 

2、全球統一自動增長列:

優點:存儲空間保持不變,業務代碼改造很小

缺點:自動增長列獲取成本很高,系統延遲很高,依賴於一個自動增長分配的單點。不具備工程落地性

 

3、各個區域劃分區段:例如某區域0~5億、5~10億

優點:業務代碼不用做任何改造。

缺點:劃分區域後,會涉及用光的問題,用光後就給後面的接手人埋坑,後面人知道了可以改造,不知道就成了背鍋俠,尤其一個系統經歷多次轉手後很多人可能還會對這類坑記得很清楚。所以,工程可落地,但留下很多坑

 

我們試圖去尋找一種:不埋坑、業務改造小、存儲空間幾乎不上升的綜合方法呢?

方法是有的,過程會相對複雜,下面一步一步完成。

第一步:由於區段的思路是讓代碼無改造的,所以我們還是需要沿用區段的思路,但是會稍加變化,沿用區段思路第一件事情是:凡是涉及到這類自增ID,程序不能單純使用4字節的int,必須使用8字節的bigint或long,不然區段很容易浪費光掉。那麼接下來就是如何避免區段的缺點,不要去埋坑。

第二步:動態區段劃分,也就是此時不再劃分某個區域是0~5億、5~10億等等,而競爭劃分,詳細步驟如下:

1、設立一箇中心節點,中心節點自動主備集羣維護。中心節點維護一個序列中心。

2、各個區域每次取走一個分片,各個區域每次取分片根據自己的寫操作頻率可以取不同大小的分片。取分片的手段可以通過樂觀鎖機制來完成,先讀,後嘗試取。例如併發量高的可以一次取走1億,併發量低的就取走100萬。各個區域取走分片後,放在本地的一個sequence表中,例如1~1億或100萬~1100萬等等均可,存放基於序列唯一標識符最小值、最大值兩個信息(也可以存放當前值,不過做法不一樣)。由於這個分片非常大,足可以讓一個地區用一個月乃至半年一年,所以對這個中心表的併發是非常小的,根據不同地區可以設置不同的分片的大小,可以充分的利用sequence的範圍。

4、例如某個區域內某個sequence的範圍是:1~1億,3億~4億,6~7億。每一個分片有一個狀態標識:未使用,開始使用,使用過半,即將結束,使用完等標識符。如果多個分片未使用,則按照順序使用。

5、當上一分片使用結束,使用新分片時(或開始使用第一個分片的時候),每個應用服務器通過判定上億分片的最大值已突破,不能再使用,此時獲取下一個分片,並通過了樂觀鎖在下一個分片中取出一部分值(將當前值改大,例如+100),此時會在短暫時間內強制寫入一部分ID值。

6、當這些指定的ID值被使用光以後,再插入數據的時候,就可以不指定ID了,可以使用DB內置的AUTO_INCREMENT,通常情況下DB會在這個時候會從最大值開始向上疊加。當然我們可以繼續沿用方法5,每次每臺機器取100、1000、10000的範圍出來給自己使用,來實現指定的自增,不過這種方式在應用重啓的時候最壞情況會浪費掉:服務器數量 * sequence cache的自增列。

總之方法是:大範圍包小範圍,小範圍內,可以在強制指定一些ID後,繼續沿用DB的AUTO_INCREMENT,也可以繼續在區域內不斷使用小範圍獲取方式,將鎖粒度降低,並在同等鎖粒度條件下,通過一些異步機制將鎖儘可能降低到0概率。分片徵用示意圖如下所示:

 

1、全局分片範圍跨度較大,所以可以使用異步任務去完成分片的預處理,這樣跨地區獲取分片的時間就不會加在業務中了,業務中使用的始終是本地分片。

2、同時,由於各個地區適應分片的速度不一樣,所以全局分片鎖粒度幾乎爲0。

3、各個區域內部多臺服務器徵用,根據業務併發性可以設置不同的步長來滿足需求,根據業務運行的隨機性,在併發行鎖上的概率也會被大大降低,這裏徵用本地分片也可以用Redis等緩存來實現。

 

 

 

 

 

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