天天看點

資料庫分庫分表(sharding)系列(二) 全局主鍵生成政策

推薦:部落客曆時三年傾注大量心血創作的《大資料平台架構與原型實作:資料中台建設實戰》一書已由知名IT圖書品牌電子工業出版社博文視點出版發行,真誠推薦給每一位讀者!點選《重磅推薦:建大資料平台太難了!給我發個工程原型吧!》了解圖書詳情,掃碼進入京東手機購書頁面!

資料庫分庫分表(sharding)系列(二) 全局主鍵生成政策

本文将主要介紹一些常見的全局主鍵生成政策,然後重點介紹flickr使用的一種非常優秀的全局主鍵生成方案。關于分庫分表(sharding)的拆分政策和實施細則,請參考該系列的前一篇文章:資料庫分庫分表(sharding)系列(一) 拆分實施政策和示例示範 本文原文連接配接: http://blog.csdn.net/bluishglc/article/details/7710738 ,轉載請注明出處!

第一部分:一些常見的主鍵生成政策

一旦資料庫被切分到多個實體結點上,我們将不能再依賴資料庫自身的主鍵生成機制。一方面,某個分區資料庫自生成的ID無法保證在全局上是唯一的;另一方面,應用程式在插入資料之前需要先獲得ID,以便進行SQL路由。目前幾種可行的主鍵生成政策有:

1. UUID:使用UUID作主鍵是最簡單的方案,但是缺點也是非常明顯的。由于UUID非常的長,除占用大量存儲空間外,最主要的問題是在索引上,在建立索引和基于索引進行查詢時都存在性能問題。

2. 結合資料庫維護一個Sequence表:此方案的思路也很簡單,在資料庫中建立一個Sequence表,表的結構類似于:

CREATE TABLE `SEQUENCE` (
	`tablename` varchar(30) NOT NULL,
	`nextid` bigint(20) NOT NULL,
	PRIMARY KEY (`tablename`)
) ENGINE=InnoDB 
           

每當需要為某個表的新紀錄生成ID時就從Sequence表中取出對應表的nextid,并将nextid的值加1後更新到資料庫中以備下次使用。此方案也較簡單,但缺點同樣明顯:由于所有插入任何都需要通路該表,該表很容易成為系統性能瓶頸,同時它也存在單點問題,一旦該表資料庫失效,整個應用程式将無法工作。有人提出使用Master-Slave進行主從同步,但這也隻能解決單點問題,并不能解決讀寫比為1:1的通路壓力問題。

除此之外,還有一些方案,像對每個資料庫結點分區段劃分ID,以及網上的一些ID生成算法,因為缺少可操作性和實踐檢驗,本文并不推薦。實際上,接下來,我們要介紹的是Fickr使用的一種主鍵生成方案,這個方案是目前我所知道的最優秀的一個方案,并且經受了實踐的檢驗,可以為大多數應用系統所借鑒。

第二部分:一種極為優秀的主鍵生成政策

flickr開發團隊在2010年撰文介紹了flickr使用的一種主鍵生成測政策,同時表示該方案在flickr上的實際運作效果也非常令人滿意,原文連接配接:Ticket Servers: Distributed Unique Primary Keys on the Cheap 這個方案是我目前知道的最好的方案,它與一般Sequence表方案有些類似,但卻很好地解決了性能瓶頸和單點問題,是一種非常可靠而高效的全局主鍵生成方案。

資料庫分庫分表(sharding)系列(二) 全局主鍵生成政策

圖1. flickr采用的sharding主鍵生成方案示意圖(點選檢視大圖)

flickr這一方案的整體思想是:建立兩台以上的資料庫ID生成伺服器,每個伺服器都有一張記錄各表目前ID的Sequence表,但是Sequence中ID增長的步長是伺服器的數量,起始值依次錯開,這樣相當于把ID的生成散列到了每個伺服器節點上。例如:如果我們設定兩台資料庫ID生成伺服器,那麼就讓一台的Sequence表的ID起始值為1,每次增長步長為2,另一台的Sequence表的ID起始值為2,每次增長步長也為2,那麼結果就是奇數的ID都将從第一台伺服器上生成,偶數的ID都從第二台伺服器上生成,這樣就将生成ID的壓力均勻分散到兩台伺服器上,同時配合應用程式的控制,當一個伺服器失效後,系統能自動切換到另一個伺服器上擷取ID,進而保證了系統的容錯。

關于這個方案,有幾點細節這裡再說明一下:

1. flickr的資料庫ID生成伺服器是專用伺服器,伺服器上隻有一個資料庫,資料庫中表都是用于生成Sequence的,這也是因為auto-increment-offset和auto-increment-increment這兩個資料庫變量是資料庫執行個體級别的變量。

2. flickr的方案中表格中的stub字段隻是一

個char(1) NOT NULL存根字段,并非表名,是以,一般來說,一個Sequence表隻有一條紀錄,可以同時為多張表生成ID,如果需要表的ID是有連續的,需要為該表單獨建立

Sequence表

3. 方案使用了mysql的LAST_INSERT_ID()函數,這也決定了Sequence表隻能有一條記錄。

4. 使用REPLACE INTO插入資料,這是很讨巧的作法,主要是希望利用mysql自身的機制生成ID,不僅是因為這樣簡單,更是因為我們需要ID按照我們設定的方式(初值和步長)來生成。

5. SELECT LAST_INSERT_ID()必須要于REPLACE INTO語句在同一個資料庫連接配接下才能得到剛剛插入的新ID,否則傳回的值總是0

6. 該方案中Sequence表使用的是MyISAM引擎,以擷取更高的性能,注意:MyISAM引擎使用的是表級别的鎖,MyISAM對表的讀寫是串行的,是以不必擔心在并發時兩次讀取會得到同一個ID(另外,應該程式也不需要同步,每個請求的線程都會得到一個新的connection,不存在需要同步的共享資源)。經過實際對比測試,使用一樣的Sequence表進行ID生成,MyISAM引擎要比InnoDB表現高出很多!

7. 可使用純JDBC實作對Sequence表的操作,以便獲得更高的效率,實驗表明,即使隻使用Spring JDBC性能也不及純JDBC來得快!

實作該方案,應用程式同樣需要做一些處理,主要是兩方面的工作:

1. 自動均衡資料庫ID生成伺服器的通路

2. 確定在某個資料庫ID生成伺服器失效的情況下,能将請求轉發到其他伺服器上執行。

相關閱讀:

資料庫分庫分表(sharding)系列(五) 一種支援自由規劃無須資料遷移和修改路由代碼的Sharding擴容方案

資料庫分庫分表(sharding)系列(四) 多資料源的事務處理

資料庫分庫分表(sharding)系列(三) 關于使用架構還是自主開發以及sharding實作層面的考量

資料庫分庫分表(sharding)系列(二) 全局主鍵生成政策

資料庫分庫分表(sharding)系列(一) 拆分實施政策和示例示範

關于垂直切分Vertical Sharding的粒度

資料庫Sharding的基本思想和切分政策