
上面圖中所描述的只是最簡單的一種情況(取第一頁數(shù)據(jù)),看起來對性能的影響并不大。但是,如果想取出第10頁數(shù)據(jù),情況又將變得復雜很多,如下圖所示:

有些讀者可能并不太理解,為什么不能像獲取第一頁數(shù)據(jù)那樣簡單處理(排序取出前10條再合并、排序)。其實并不難理解,因為各分片節(jié)點中的數(shù)據(jù)可能是隨機的,為了排序的準確性,必須把所有分片節(jié)點的前N頁數(shù)據(jù)都排序好后做合并,最后再進行整體的排序。很顯然,這樣的操作是比較消耗資源的,用戶越往后翻頁,系統(tǒng)性能將會越差。
跨分片的函數(shù)處理
在使用Max、Min、Sum、Count之類的函數(shù)進行統(tǒng)計和計算的時候,需要先在每個分片數(shù)據(jù)源上執(zhí)行相應的函數(shù)處理,然后再將各個結(jié)果集進行二次處理,最終再將處理結(jié)果返回。如下圖所示:

跨分片join
Join是關(guān)系型數(shù)據(jù)庫中最常用的特性,但是在分片集群中,join也變得非常復雜。應該盡量避免跨分片的join查詢(這種場景,比上面的跨分片分頁更加復雜,而且對性能的影響很大)。通常有以下幾種方式來避免:
全局表
全局表的概念之前在“垂直分庫”時提過?;舅枷胍恢?,就是把一些類似數(shù)據(jù)字典又可能會產(chǎn)生join查詢的表信息放到各分片中,從而避免跨分片的join。
ER分片
在關(guān)系型數(shù)據(jù)庫中,表之間往往存在一些關(guān)聯(lián)的關(guān)系。如果我們可以先確定好關(guān)聯(lián)關(guān)系,并將那些存在關(guān)聯(lián)關(guān)系的表記錄存放在同一個分片上,那么就能很好的避免跨分片join問題。 在一對多關(guān)系的情況下,我們通常會選擇按照數(shù)據(jù)較多的那一方進行拆分。 如下圖所示:

這樣一來,Data Node1上面的訂單表與訂單詳細表就可以直接關(guān)聯(lián),進行局部的join查詢了,Data Node2上也一樣?;贓R分片的這種方式,能夠有效避免大多數(shù)業(yè)務場景中的跨分片join問題。
內(nèi)存計算
隨著spark內(nèi)存計算的興起,理論上來講,很多跨數(shù)據(jù)源的操作問題看起來似乎都能夠得到解決??梢詫?shù)據(jù)丟給spark集群進行內(nèi)存計算,最后將計算結(jié)果返回。
跨分片事務問題
跨分片事務也分布式事務,想要了解分布式事務,就需要了解“XA接口”和“兩階段提交”。值得提到的是,MySQL5.5x和5.6x中的xa支持是存在問題的,會導致主從數(shù)據(jù)不一致。直到5.7x版本中才得到修復。Java應用程序可以采用Atomikos框架來實現(xiàn)XA事務(J2EE中JTA)。感興趣的讀者可以自行參考《分布式事務一致性解決方案》,鏈接地址:
http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency
我們的系統(tǒng)真的需要分庫分表嗎
讀完上面內(nèi)容,不禁引起有些讀者的思考,我們的系統(tǒng)是否需要分庫分表嗎?
其實這點沒有明確的判斷標準,比較依賴實際業(yè)務情況和經(jīng)驗判斷。依照筆者個人的經(jīng)驗,一般MySQL單表1000W左右的數(shù)據(jù)是沒有問題的(前提是應用系統(tǒng)和數(shù)據(jù)庫等層面設(shè)計和優(yōu)化的比較好)。當然,除了考慮當前的數(shù)據(jù)量和性能情況時,作為架構(gòu)師,我們需要提前考慮系統(tǒng)半年到一年左右的業(yè)務增長情況,對數(shù)據(jù)庫服務器的QPS、連接數(shù)、容量等做合理評估和規(guī)劃,并提前做好相應的準備工作。如果單機無法滿足,且很難再從其他方面優(yōu)化,那么說明是需要考慮分片的。這種情況可以先去掉數(shù)據(jù)庫中自增ID,為分片和后面的數(shù)據(jù)遷移工作提前做準備。
很多人覺得“分庫分表”是宜早不宜遲,應該盡早進行,因為擔心越往后公司業(yè)務發(fā)展越快、系統(tǒng)越來越復雜、系統(tǒng)重構(gòu)和擴展越困難…這種話聽起來是有那么一點道理,但我的觀點恰好相反,對于關(guān)系型數(shù)據(jù)庫來講,我認為“能不分片就別分片”,除非是系統(tǒng)真正需要,因為數(shù)據(jù)庫分片并非低成本或者免費的。