如何在高並發分布式系統中產生全域唯一Id

來源:互聯網
上載者:User

http://www.cnblogs.com/lsx1993/p/4663125.html

《分布式環境下資料庫主鍵方案》

[ http://www.2cto.com/database/201309/243195.html ]

在只使用單資料庫時,使用自增主鍵ID無疑是最適合的。但在叢集、主從架構上時就會有一些問題,比如:主鍵的全域唯一。

叢集環境下除了自增ID外的其它建立主鍵的方案

1、通過應用程式產生一個GUID,然後和資料一起插入切分後的叢集。
優點是維護簡單,實現也容易。缺點是應用的計算成本較大,且GUID的長度比較長,佔用資料庫儲存空間較大,涉及到應用的開發。
說明:主要優勢是簡單,缺點是浪費儲存空間。

2、通過獨立的應用程式事先在資料庫中產生一系列唯一的 ID,各應用程式通過介面或者自己去讀取再和資料一起插入到切分後的叢集中。
優點是全域唯一主鍵簡單,維護相對容易。
缺點是實現複雜,需要應用開發。
說明:ID表要頻繁查和頻繁更新,插入資料時,影響效能。

3、通過【中樞資料庫伺服器】利用資料庫自身的自增類型(如 MySQL的 auto_increment 欄位),或者自增對象(如 Oracle 的 Sequence)等先產生一個唯一 ID 再和資料一起插入切分後的叢集。優點是。好像沒有特別明顯的優點。缺點是實現較為複雜,且整體可用性維繫在這個中樞資料庫伺服器上,一旦這裡crash 了,所有的叢集都無法進行插入操作,涉及到應用開發。
說明:不推薦。

4、通過叢集編號加叢集內的自增(auto_increment類型)【兩個欄位】共同組成唯一主鍵。優點是實現簡單,維護也比較簡單,對應用透明。
缺點是引用關聯操作相對比較複雜,需要兩個欄位,主鍵佔用空間較大,在使用 InnoDB 的時候這一點的副作用很明顯。
說明:雖然是兩個欄位,但是這方式儲存空間最小,僅僅多了一個smallint兩個位元組。

5、通過設定每個叢集中自增 ID 起始點(auto_increment_offset),將各個叢集的ID進行絕對的分段來實現全域唯一。當遇到某個叢集資料增長過快後,通過命令調整下一個 ID 起始位置跳過可能存在的衝突。優點是實現簡單,且比較容易根據 ID 大小直接判斷出資料處在哪個叢集,對應用透明。缺點是維護相對較複雜,需要高度關注各個叢集 ID 增長狀況。
說明:段滿了,調整太麻煩。

6、通過設定每個叢集中自增 ID 起始點(auto_increment_offset)以及 ID 自增步長(auto_increment_increment),讓目前每個叢集的起始點錯開 1,步長選擇大於將來基本不可能達到的切分叢集數,達到將 ID 相對分段的效果來滿足全域唯一的效果。優點是實現簡單,後期維護簡單,對應用透明。缺點是第一次設定相對較為複雜。
說明:避免重合需要多種方案結合


【使用UUID作為主鍵帶來的問題】

對於InnoDB這種聚集主鍵類型的引擎來說,資料會按照主鍵進行排序,由於UUID的無序性,InnoDB會產生巨大的IO壓力,此時不適合使用UUID做物理主鍵,可以把它作為邏輯主鍵,物理主鍵依然使用自增ID。

首先,innodb會對主鍵進行物理排序,這對auto_increment_int是個好訊息,因為後一次插入的主鍵位置總是在最後。但是對uuid來 說,這卻是個壞訊息,因為uuid是雜亂無章的,每次插入的主鍵位置是不確定的,可能在開頭,也可能在中間,在進行主鍵物理排序的時候,勢必會造成大量的 IO操作影響效率。

這個問題解決起來辦法不多,比較常見的方式是主鍵仍然用auto_increment_int來做,而另加一個uuid做唯一索引,表外部索引鍵關聯什麼的,還 用uuid來做,也就是說auto_increment_int只是一個形式上的主鍵,而uuid才是事實上的主鍵,這樣,一方面int主鍵不會浪費太多空間,另一方面,還可以繼續使用uuid。

1、最簡單的方法
4台資料庫,第一台mysql主鍵從1開始每次加4,第二台從2開始每次加4,以此類推。。

2、搭建sequence server
2.1、選用N台mysql作為sequence server,防止單點故障。
2.2、每個server上的每張表都代表一個序列,每張表也只有一條記錄(表級鎖,選用myisam引擎)。
2.3、第一台server的序列從1開始每次加N,第二台從2開始每次加N。

2.4、擷取時先從第一台server上擷取nextVal並修改nextVal加N,如果第一台Server擷取失敗,則從第二台Server上擷取。。

修改MySQL【預設自動成長的步長】
set global auto_increment_increment=1; — 設定序列的增長值
show global variables; — 顯示所有的global變數
show global variables like ‘%test%’ — 查詢包含test字串的global變數
《如何在高並發分布式系統中產生全域唯一Id》

使用資料庫自增Id

優勢:編碼簡單,無需考慮記錄唯一標識的問題。

缺陷:

1) 在大表做水平分表時,就不能使用自增Id,因為Insert的記錄插入到哪個分表依分表規則判定決定,若是自增Id,各個分表中Id就會重複,在做查詢、刪除時就會有異常。

2) 在對錶進行高並發單記錄插入時需要加入事物機制,否則會出現Id重複的問題。

3) 在業務上操作父、子表(即關聯表)插入時,需要在插入資料庫之前擷取max(id)用於標識父表和子表關係,若存在並發擷取max(id)的情況,max(id)會同時被別的線程擷取到。

4) 等等。

結論:適合小應用,無需分表,沒有高並發效能要求。

 

 

單獨開一個資料庫,擷取全域唯一的自增序號或各表的MaxId

1) 使用自增序號表

專門一個資料庫,產生序號。開啟事物,每次操作插入時,先將資料插入到序列表並返回自增序號用於做為唯一Id進行業務資料插入。

注意:需要定期清理序列表的資料以保證擷取序號的效率;插入序列表記錄時要開啟事物。

使用此方案的問題是:每次的查詢序號是一個效能損耗;如果這個序號列暴了,那就杯具了,你不知道哪個表使用了哪個序列,所以就必須換另一種唯一Id方式如GUID。

 

2) 使用MaxId表格儲存體各表的MaxId值

專門一個資料庫,記錄各個表的MaxId值,建一個預存程序來取Id,邏輯大致為:開啟事務,對於在表中不存在記錄,直接返回一個預設值為1的索引值,同時插入該條記錄到table_key表中。而對於已存在的記錄,key值直接在原來的key基礎上加1更新到MaxId表中並返回key。

使用此方案的問題是:每次的查詢MaxId是一個效能損耗;不過不會像自增序列表那麼容易列暴掉,因為是擺表進行劃分的。

詳細可參考:《使用MaxId表格儲存體各表的MaxId值,以擷取全域唯一Id》

我截取此文中的sql文法如下:

第一步:建立表

 

1 2 3 4 5 create table table_key ( table_name    varchar (50) notnullprimarykey, key_value    intnotnull )

 

第二步:建立預存程序來取自增ID

 

1 2 3 4 5 6

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.