在之前的文章中,我介紹了分庫分表的幾種表現(xiàn)形式和玩法,也重點介紹了垂直分庫所帶來的問題和解決方法。本篇中,我們將繼續(xù)聊聊水平分庫分表的一些技巧。
分片技術(shù)的由來
關(guān)系型數(shù)據(jù)庫本身比較容易成為系統(tǒng)性能瓶頸,單機存儲容量、連接數(shù)、處理能力等都很有限,數(shù)據(jù)庫本身的“有狀態(tài)性”導(dǎo)致了它并不像Web和應(yīng)用服務(wù)器那么容易擴展。在互聯(lián)網(wǎng)行業(yè)海量數(shù)據(jù)和高并發(fā)訪問的考驗下,聰明的技術(shù)人員提出了分庫分表技術(shù)(有些地方也稱為Sharding、分片)。同時,流行的分布式系統(tǒng)中間件(例如MongoDB、ElasticSearch等)均自身友好支持Sharding,其原理和思想都是大同小異的。
分布式全局唯一ID
在很多中小項目中,我們往往直接使用數(shù)據(jù)庫自增特性來生成主鍵ID,這樣確實比較簡單。而在分庫分表的環(huán)境中,數(shù)據(jù)分布在不同的分片上,不能再借助數(shù)據(jù)庫自增長特性直接生成,否則會造成不同分片上的數(shù)據(jù)表主鍵會重復(fù)。簡單介紹下使用和了解過的幾種ID生成算法。
Twitter的Snowflake(又名“雪花算法”)
UUID/GUID(一般應(yīng)用程序和數(shù)據(jù)庫均支持)
MongoDB ObjectID(類似UUID的方式)
Ticket Server(數(shù)據(jù)庫生存方式,F(xiàn)lickr采用的就是這種方式)
其中,Twitter 的Snowflake算法是筆者近幾年在分布式系統(tǒng)項目中使用最多的,未發(fā)現(xiàn)重復(fù)或并發(fā)的問題。該算法生成的是64位唯一Id(由41位的timestamp+ 10位自定義的機器碼+ 13位累加計數(shù)器組成)。這里不做過多介紹,感興趣的讀者可自行查閱相關(guān)資料。
常見分片規(guī)則和策略
分片字段該如何選擇
在開始分片之前,我們首先要確定分片字段(也可稱為“片鍵”)。很多常見的例子和場景中是采用ID或者時間字段進(jìn)行拆分。這也并不絕對的,我的建議是結(jié)合實際業(yè)務(wù),通過對系統(tǒng)中執(zhí)行的sql語句進(jìn)行統(tǒng)計分析,選擇出需要分片的那個表中最頻繁被使用,或者最重要的字段來作為分片字段。
常見分片規(guī)則
常見的分片策略有隨機分片和連續(xù)分片這兩種,如下圖所示:

當(dāng)需要使用分片字段進(jìn)行范圍查找時,連續(xù)分片可以快速定位分片進(jìn)行高效查詢,大多數(shù)情況下可以有效避免跨分片查詢的問題。后期如果想對整個分片集群擴容時,只需要添加節(jié)點即可,無需對其他分片的數(shù)據(jù)進(jìn)行遷移。但是,連續(xù)分片也有可能存在數(shù)據(jù)熱點的問題,就像圖中按時間字段分片的例子,有些節(jié)點可能會被頻繁查詢壓力較大,熱數(shù)據(jù)節(jié)點就成為了整個集群的瓶頸。而有些節(jié)點可能存的是歷史數(shù)據(jù),很少需要被查詢到。
隨機分片其實并不是隨機的,也遵循一定規(guī)則。通常,我們會采用Hash取模的方式進(jìn)行分片拆分,所以有些時候也被稱為離散分片。隨機分片的數(shù)據(jù)相對比較均勻,不容易出現(xiàn)熱點和并發(fā)訪問的瓶頸。但是,后期分片集群擴容起來需要遷移舊的數(shù)據(jù)。使用一致性Hash算法能夠很大程度的避免這個問題,所以很多中間件的分片集群都會采用一致性Hash算法。離散分片也很容易面臨跨分片查詢的復(fù)雜問題。
數(shù)據(jù)遷移,容量規(guī)劃,擴容等問題
很少有項目會在初期就開始考慮分片設(shè)計的,一般都是在業(yè)務(wù)高速發(fā)展面臨性能和存儲的瓶頸時才會提前準(zhǔn)備。因此,不可避免的就需要考慮歷史數(shù)據(jù)遷移的問題。一般做法就是通過程序先讀出歷史數(shù)據(jù),然后按照指定的分片規(guī)則再將數(shù)據(jù)寫入到各個分片節(jié)點中。
此外,我們需要根據(jù)當(dāng)前的數(shù)據(jù)量和QPS等進(jìn)行容量規(guī)劃,綜合成本因素,推算出大概需要多少分片(一般建議單個分片上的單表數(shù)據(jù)量不要超過1000W)。
如果是采用隨機分片,則需要考慮后期的擴容問題,相對會比較麻煩。如果是采用的范圍分片,只需要添加節(jié)點就可以自動擴容。
跨分片技術(shù)問題
跨分片的排序分頁
一般來講,分頁時需要按照指定字段進(jìn)行排序。當(dāng)排序字段就是分片字段的時候,我們通過分片規(guī)則可以比較容易定位到指定的分片,而當(dāng)排序字段非分片字段的時候,情況就會變得比較復(fù)雜了。為了最終結(jié)果的準(zhǔn)確性,我們需要在不同的分片節(jié)點中將數(shù)據(jù)進(jìn)行排序并返回,并將不同分片返回的結(jié)果集進(jìn)行匯總和再次排序,最后再返回給用戶。如下圖所示: