摘 要: 針對如何在非分布式數據庫管理系統中應用分布式特性,提出了分布式數據層中間件DDLM的設計方案。在數據持久化框架和JDBC之間引入一個分庫分表的中間件,從而把數據拆分到多個數據庫的多個表中,在用戶看來這些數據仍然存在于一張表中,從而在應用層透明地解決了海量數據的讀寫問題。
關鍵詞: 分布式數據層;邏輯表;物理表
隨著互聯網應用業務的高速增長,搜索引擎、電子商務、門戶網站等大型互聯網公司的網絡信息流量直線上升,日訪問量甚至突破億次大關,從而產生了海量信息和對這些信息的海量讀寫,集中式數據庫越來越難以滿足互聯網公司對海量信息的高可靠性、高擴展性的需求。
分布式數據庫通過對數據進行垂直分片和水平分片,讓數據存儲在多個數據庫中,能夠解決海量數據的存儲和管理問題。所謂垂直分片是把一個全局關系的屬性集分成若干子集,并在這些子集上作投影運算,每個投影稱為垂直分片。屬性集數目是一定的,垂直拆分只能適合一定規模的擴展,當對每個垂直分片的訪問超過單數據庫所能承受的負載時,就需要水平分片。水平分片是按一定的條件把全局關系的所有元組劃分成若干不相交的子集,每個子集為關系的一個片段。
目前市場上Oracle、DB2等商用分布式數據庫的價格昂貴,一般企業僅僅將商用分布式數據庫用來管理企業最核心的數據,而非核心的數據則存放在PostgreSQL、MySql等開源數據庫中。然而大多數開源數據庫分布式功能不夠強大,甚至不具備分布式的功能。為了解決這個問題,本文提出了分布式數據層中間件的設計方案,在應用層把數據垂直、水平拆分到多個數據庫、多張表中,使應用層具備了分布式的功能,和底層數據庫是否具有分布式特性沒有關系,從而使底層的開源數據庫能夠通過分布式數據層中間件具有分布式的特性。
1 分布式數據層中間件的設計原理
傳統的持久化框架是基于JDBC的,如JPA(Java Persistence API)、Hibernate和TopLink等。對象關系映射(ORM)框架是根據對象的屬性生成Sql語句,然后調用JDBC API完成數據的持久化操作。Ibatis是個JDBC模板,相當于半自動化ORM映射工具,也是調用JDBC接口來完成對數據的持久化操作的。
所有Java持久化框架對數據庫的持久化操作都是直接或者間接地調用JDBC API執行Sql語句來完成對數據的CRUD操作,每條Sql語句通常只操作單數據庫。在持久化框架(如Hibernate)和JDBC之間設計一個分布式數據層中間件DDLM(Distributed Data Layer MiddleWare),DDLM層把業務邏輯層的每條Sql語句(下文記作邏輯Sql語句)按照垂直、水平拆分的策略解釋成多個Sql語句,解釋后的每條Sql語句(下文記作物理Sql語句)對一個數據源進行操作,從而一條邏輯Sql語句被解釋成多條物理Sql語句,因此DDLM具有分布式的特性。
分布式數據層中間件的原理如圖1所示。圖中把持久化層分為四個子層:持久化框架、分布式數據層、JDBC、數據庫。例如:JPA把根據ORM映射規則生成的Sql語句交給分布式數據層,分布式數據層中間件把Sql語句解釋為多個物理Sql語句交給JDBC接口,JDBC接口完成對數據庫的CRUD操作。

這樣分布式數據層就能夠完成原本只有分布式數據庫才能完成的垂直分片、水平分片、合并排序等分布式操作。用戶不需要使用新的管理工具,只需要利用原有數據庫的管理工具與分布式數據層中間件交互。該層把對多個物理數據庫的操作透明化。
2 分布式數據層中間件的設計方案
在DDLM設計中,不需進行垂直分片。一個全局關系對應一張數據庫表,這樣就能滿足應用中的大多數需求。而且按照一個關系映射一張表的原則拆分,邏輯簡單清晰,簡化了數據庫模型的設計。DDLM的研究重點是對表的水平分片以及水平分片后產生的問題的解決。
水平分片把關系模式R的記錄拆分到n(n≥1)個物理數據庫中,每個物理數據庫有m(m≥1)張數據表,模式R的記錄被路由到n×m張模式相同的物理數據庫表中。
水平分片后,記錄存在于不同的物理數據庫,隨之產生了兩個問題:查詢數據時需要合并并且排序、主鍵需要全局唯一生成。
2.1 分庫策略
一個數據庫所能存放的表數目會受到文件系統的限制,有必要把一張邏輯表的數據拆分到多個物理數據庫中。為了實現此功能,在表模式中添加一個整數類型的db_num字段,db_num字段的值指示了記錄(也稱作元組)被路由的目標數據庫。下面舉例說明db_num字段的作用:
設關系模式為R(id,…,db_num,…),該模式對應的表的數據需要被路由到N(N×1)個物理數據庫內,任意一條記錄(id_value,…,n,…)存在于第n個物理數據庫的某張表中(0<n≤N,n為db_num字段的值)。
2.2 分表策略
數據庫表存放記錄數量的最大值在理論上可以取很大的值,但在實際應用中通常受到文件系統的限制。當一張表的數據記錄數達到一個閾值時,操作該表的速率會急劇下降。在MySql數據庫中,當表記錄數達到1 000萬條時,查詢該表的速率明顯地下降。
在同一個數據庫建立多張模式相同的表,數據被路由到不同的表中,從而可以很好地解決表記錄過多引起速率下降的問題。每條記錄要唯一地標示它所在表的編號,因此必須引入某種編碼手段存放該記錄的編號。有兩種常用的策略:(1)用記錄的主鍵標示該記錄所在表的編號,也就是數據庫表主鍵拆分策略;(2)特意引進一個日期字段標示記錄所在表的編號,也就是數據庫表日期字段拆分策略。
2.2.1 數據庫表主鍵拆分策略
假設邏輯表模式R的記錄在一個數據庫中需要分別路由到M(M≥1)張物理表中,設邏輯表R的表名為logic_table_name,物理表的表名分別是table_1,table_2, …,table_M。
設表R的模式為R(id,…),其中id是模式的主鍵,其數據類型為整數類型。R的任意一條記錄r(x,…),其主鍵值為x,r被路由到物理表table_y中(y的值為x和M取模的結果,即:y=x%M)。
隨著記錄主鍵值id的增加,記錄可以非常均勻地路由到M張物理表中。然而,如果需要動態增加M的值,如M的值由M增加到M’,則記錄就不會均勻地分配到M’張物理表中。此時可以采取表日期字段拆分法。
2.2.2 數據庫表日期字段拆分
按照表的日期字段拆分數據是另一種常用的拆分策略,當數據量比較大時,暫時無法估算到底需要多少張物理表才能存放一個模式的所有記錄,此時可以采取按表日期字段拆分策略。
設數據庫表模式為R(id,column1,…,update_time),update_time字段是該記錄創建時的系統時間,任意一條記錄r(x,column1_vlaue,…,update_time_value)。在應用層讀取系統的時間可以計算得到update_time_value時間值是一年中的某天day_of_year,這樣就可以把數據拆分到365(或366)張表中,物理表名分別為table_name_0,table_name_1,…,table_name_day_of_year,…,table_name_365(或365)。
除了按照取得update_time_value的day_of_year值,也可以取得update_time_value在星期中的某天day_of_week和在月的某天day_of_month。DDLM中間支持按照時間的各種策略。
為了最大化地拆分數據,DDLM還提供以上策略的二級拆分。
2.3 數據合并排序策略
分庫分表后,一張邏輯表table_name的數據存儲在不同的物理表中,在對表進行查詢、刪除和更新時,一條Sql語句可能會同時對一張或者多張物理表的數據產生影響。對于刪除、更新操作,分別針對每個物理數據庫執行對應的刪除、更新語句,然而對于查詢語句涉及到多個物理數據庫時,不能簡單地針對每個數據庫執行查詢語句,還需要合并所有的查詢結果并且排序。下面舉例說明查詢合并以及排序策略。
假設物理表表名分別為table_name0,table_name1, …,table_nameN,同時設Sql語句為SELECT*FROM talbe_name WHERE update_time=today OR update_time=yesterday ORDER By id LIMIT a,b(其中a,b為自然數),又假設該邏輯表table_name是按update_time日期字段水平分片的,則Sql的查詢會涉及到物理表中的兩張表記為table_nameX(0≤X≤N),table_name_Y(0≤X≤N)。該Sql的執行流程如下:
1)對表table_nameX查詢操作SELECT*FROM table_nameX WHERE update_time=today得到結果集ResultSet1,并對表table_nameY執行和SELECT*FROM table_nameY WHERE update_time=yesterday得到結果集ResultSet2。
(2)從結果ResultSet1和ResultSet2讀取數據存放在一個集合Result中,按照id字段排序。
(3)在集合Result中,讀取id分布在區間[a,a+b]上的記錄作為返回結果。
通過上述查詢合并排序策略,當在查詢過程中涉及到多張物理表時,能夠分別讀取多張物理數據庫表的數據,然后在內存中對數據分別執行合并、排序和分頁操作。合并排序需要一定的時間和空間,所以在查詢時,盡量不要同時涉及到兩個或者以上的數據庫。
2.4 主鍵生成策略設計
在DDLM中,數據被路由到多個數據庫的多張表中,為了確保主鍵的全局唯一性,不能借助于數據庫管理系統DBMS來生成主鍵,因為DBMS生成的主鍵只在當前數據庫中具有唯一性,不能確保主鍵的全局唯一性。有兩種策略可以生成具有全局唯一性的主鍵:(1)采用通用的UUID生成策略,UUID是借助主機的時間戳、IP地址和網卡Mac地址等生成分布式唯一標示符的算法,但是該策略生成的唯一標示符需要用32個字符來存儲,非常浪費空間;(2)借助分庫分表的信息生成主鍵,該策略非常有效地利用了分庫分表的路由信息,巧妙地生成全局唯一主鍵。下面將詳細地介紹該策略。
假設一張邏輯表logic_name的數據分別存儲在數據庫db_1,db_2,…,db_s(s為大于1的正整數)中,每個數據庫中有相同的表table_1,table_2,…,table_t(t為大于1的正整數)。用三位作為數據庫的編號、三位作為表的編號以及一個隨機字段來構成全局唯一主鍵。數學表達式為xxxyyym…m,xxx為數據庫的編號,yyy為表的編號,m…m為隨機數。該主鍵生成策略有兩個優點:(1)實現方便,通常一張邏輯表的數據不會多得需要被路由到1 000個物理數據庫以上,也不會路由到1 000張表以上;(2)主鍵本身就包含有路由信息。使用此策略,由主鍵信息就能路由該記錄,而不必查詢配置信息。假設一條數據庫記錄的主鍵為10020012345,取出前三位為100,則該記錄應該路由到編號為100的數據庫(記為db100),取出4~6位為200,則該記錄應該路由到db100的編號為200的表。
DDLM在應用層透明地把邏輯數據庫表的數據拆分到多個物理數據庫的多張表中,同時提供合并查詢排序、主鍵生成等功能,從而可以在不支持分布特性的數據庫管理系統應用分布式特性。
參考文獻
[1] 林昊.分布式Java應用:基礎與實踐[M].北京:電子工業出版社,2010.
[2] 何坤.基于內存數據庫的分布式數據庫架構[J].程序員,2010(7):116.
[3] 潘群華,吳秋云,陳宏盛.分布式數據庫系統中數據一致性的維護方法[J].計算機工程,2002(9):12-15.
[4] 習周龍.分布式數據庫管理系統實現技術[M].北京:科學出版社,1999.
[5] 趙致格.數據庫系統與應用[M].北京:高等教育出版社,1994.
