新聞中心
本文將以“帖子中心”為例,介紹“1對多”類業(yè)務(wù),隨著數(shù)據(jù)量的逐步增大,數(shù)據(jù)庫性能顯著降低,數(shù)據(jù)庫水平切分相關(guān)的架構(gòu)實(shí)踐:

成都創(chuàng)新互聯(lián)公司作為成都網(wǎng)站建設(shè)公司,專注成都網(wǎng)站建設(shè)公司、網(wǎng)站設(shè)計(jì),有關(guān)企業(yè)網(wǎng)站制作方案、改版、費(fèi)用等問題,行業(yè)涉及成都主動防護(hù)網(wǎng)等多個領(lǐng)域,已為上千家企業(yè)服務(wù),得到了客戶的尊重與認(rèn)可。
- 如何來實(shí)施水平切分
- 水平切分后常見的問題
- 典型問題的優(yōu)化思路及實(shí)踐
一、什么是1對多關(guān)系
所謂的“1對1”,“1對多”,“多對多”,來自數(shù)據(jù)庫設(shè)計(jì)中的“實(shí)體-關(guān)系”ER模型,用來描述實(shí)體之間的映射關(guān)系。
1對1
- 一個用戶只有一個登錄名,一個登錄名只對應(yīng)一個用戶
- 一個uid對應(yīng)一個login_name,一個login_name只對應(yīng)一個uid
這是一個1對1的關(guān)系。
1對多
- 一個用戶可以發(fā)多條微博,一條微博只有一個發(fā)送者
- 一個uid對應(yīng)多個msg_id,一個msg_id只對應(yīng)一個uid
這是一個1對多的關(guān)系。
多對多
- 一個用戶可以關(guān)注多個用戶
- 一個用戶也可以被多個粉絲關(guān)注
這是一個多對多的關(guān)系。
二、帖子中心業(yè)務(wù)分析
帖子中心是一個典型的1對多業(yè)務(wù)。
一個用戶可以發(fā)布多個帖子,一個帖子只對應(yīng)一個發(fā)布者。
任何脫離業(yè)務(wù)的架構(gòu)設(shè)計(jì)都是耍流氓,先來看看帖子中心對應(yīng)的業(yè)務(wù)需求。
帖子中心,是一個提供帖子發(fā)布/修改/刪除/查看/搜索的服務(wù)。
寫操作:
- 發(fā)布(insert)帖子
- 修改(update)帖子
- 刪除(delete)帖子
讀操作:
- 通過tid查詢(select)帖子實(shí)體,單行查詢
- 通過uid查詢(select)用戶發(fā)布過的帖子,列表查詢
- 帖子檢索(search),例如通過時間、標(biāo)題、內(nèi)容搜索符合條件的帖子
在數(shù)據(jù)量較大,并發(fā)量較大的時候,通常通過元數(shù)據(jù)與索引數(shù)據(jù)分離的架構(gòu)來滿足不同類型的需求:
架構(gòu)中的幾個關(guān)鍵點(diǎn):
- tiezi-center:帖子服務(wù)
- tiezi-db:提供元數(shù)據(jù)存儲
- tiezi-search:帖子搜索服務(wù)
- tiezi-index:提供索引數(shù)據(jù)存儲
- MQ:tiezi-center與tiezi-search通訊媒介,一般不直接使用RPC調(diào)用,而是通過MQ對兩個子系統(tǒng)解耦(為何這么解耦,請參見《到底什么時候該使用MQ?》)
其中,tiezi-center和tiezi-search分別滿足兩類不同的讀需求:
如上圖所示:
- tid和uid上的查詢需求,可以由tiezi-center從元數(shù)據(jù)讀取并返回
- 其他類檢索需求,可以由tiezi-search從索引數(shù)據(jù)檢索并返回
對于寫需求:
如上圖所示:
- 增加,修改,刪除的操作都會從tiezi-center發(fā)起
- tiezi-center修改元數(shù)據(jù)
- tiezi-center將信息修改通知發(fā)送給MQ
- tiezi-search從MQ接受修改信息
- tiezi-search修改索引數(shù)據(jù)
tiezi-search,搜索架構(gòu)不是本文的重點(diǎn)(外置索引架構(gòu)設(shè)計(jì),請參見《100億數(shù)據(jù)1萬屬性數(shù)據(jù)架構(gòu)設(shè)計(jì)》),后文將重點(diǎn)描述帖子中心元數(shù)據(jù)這一塊的水平切分設(shè)計(jì)。
三、帖子中心元數(shù)據(jù)設(shè)計(jì)
通過帖子中心業(yè)務(wù)分析,很容易了解到,其核心元數(shù)據(jù)為:
- Tiezi(tid, uid, time, title, content, …);
其中:
- tid為帖子ID,主鍵
- uid為用戶ID,發(fā)帖人
- time, title, content …等為帖子屬性
數(shù)據(jù)庫設(shè)計(jì)上,在業(yè)務(wù)初期,單庫就能滿足元數(shù)據(jù)存儲要求,其典型的架構(gòu)設(shè)計(jì)為:
- tiezi-center:帖子中心服務(wù),對調(diào)用者提供友好的RPC接口
- tiezi-db:對帖子數(shù)據(jù)進(jìn)行存儲
在相關(guān)字段上建立索引,就能滿足相關(guān)業(yè)務(wù)需求:
- 帖子記錄查詢,通過tid查詢,約占讀請求量90%
- select * from t_tiezi where tid=$tid
- select * from t_tiezi where uid=$uid
四、帖子中心水平切分-tid切分法
當(dāng)數(shù)據(jù)量越來越大時,需要對帖子數(shù)據(jù)的存儲進(jìn)行線性擴(kuò)展。
既然是帖子中心,并且帖子記錄查詢量占了總請求的90%,很容易想到通過tid字段取模來進(jìn)行水平切分:
這個方法簡單直接,優(yōu)點(diǎn):
- 100%寫請求可以直接定位到庫
- 90%的讀請求可以直接定位到庫
缺點(diǎn):
- 一個用戶發(fā)布的所有帖子可能會落到不同的庫上,10%的請求通過uid來查詢會比較麻煩
如上圖,一個uid訪問需要遍歷所有庫。
五、帖子中心水平切分-uid切分法
有沒有一種切分方法,確保同一個用戶發(fā)布的所有帖子都落在同一個庫上,而在查詢一個用戶發(fā)布的所有帖子時,不需要去遍歷所有的庫呢?
答:使用uid來分庫可以解決這個問題。
新出現(xiàn)的問題:如果使用uid來分庫,確保了一個用戶的帖子數(shù)據(jù)落在同一個庫上,那通過tid來查詢,就不知道這個帖子落在哪個庫上了,豈不是還需要遍歷全庫,需要怎么優(yōu)化呢?
答:tid的查詢是單行記錄查詢,只要在數(shù)據(jù)庫(或者緩存)記錄tid到uid的映射關(guān)系,就能解決這個問題。
新增一個索引庫:
- t_mapping(tid, uid);
- 這個庫只有兩列,可以承載很多數(shù)據(jù)
- 即使數(shù)據(jù)量過大,索引庫可以利用tid水平切分
- 這類kv形式的索引結(jié)構(gòu),可以很好的利用cache優(yōu)化查詢性能
- 一旦帖子發(fā)布,tid和uid的映射關(guān)系就不會發(fā)生變化,cache的***率會非常高
使用uid分庫,并增加索引庫記錄tid到uid的映射關(guān)系之后,每當(dāng)有uid上的查詢:
可以通過uid直接定位到庫。
每當(dāng)有tid上的查詢:
- 先查詢索引表,通過tid查詢到對應(yīng)的uid
- 再通過uid定位到庫
這個方法的優(yōu)點(diǎn):
- 一個用戶發(fā)布的所以帖子落在同一個庫上
- 10%的請求過過uid來查詢列表,可以直接定位到庫
- 索引表cache***率非常高,因?yàn)閠id與uid的映射關(guān)系不會變
缺點(diǎn):
- 90%的tid請求,以及100%的修改請求,不能直接定位到庫,需要先進(jìn)行一次索引表的查詢,當(dāng)然這個查詢非常塊,通常在5ms內(nèi)可以返回
- 數(shù)據(jù)插入時需要操作元數(shù)據(jù)與索引表,可能引發(fā)潛在的一致性問題
六、帖子中心水平切分-基因法
有沒有一種方法,既能夠通過uid定位到庫,又不需要建立索引表來進(jìn)行二次查詢呢,這就是本文要敘述的“1對多”業(yè)務(wù)分庫***實(shí)踐,基因法。
什么是分庫基因?
通過uid分庫,假設(shè)分為16個庫,采用uid%16的方式來進(jìn)行數(shù)據(jù)庫路由,這里的uid%16,其本質(zhì)是uid的***4個bit決定這行數(shù)據(jù)落在哪個庫上,這4個bit,就是分庫基因。
什么是基因法分庫?
在“1對多”的業(yè)務(wù)場景,使用“1”分庫,在“多”的數(shù)據(jù)id生成時,id末端加入分庫基因,就能同時滿足“1”和“多”的分庫查詢需求。
如上圖所示,uid=666的用戶發(fā)布了一條帖子(666的二進(jìn)制表示為:1010011010):
- 使用uid%16分庫,決定這行數(shù)據(jù)要插入到哪個庫中
- 分庫基因是uid的***4個bit,即1010
- 在生成tid時,先使用一種分布式ID生成算法生成前60bit(上圖中綠色部分)
- 將分庫基因加入到tid的***4個bit(上圖中粉色部分)
- 拼裝成最終的64bit帖子tid(上圖中藍(lán)色部分)
(怎么生成60bit分布式唯一ID,請參見《分布式ID生成算法》)
這般,保證了同一個用戶發(fā)布的所有帖子的tid,都落在同一個庫上,tid的***4個bit都相同,于是:
- 通過uid%16能夠定位到庫
- 通過tid%16也能定位到庫
潛在問題一:同一個uid發(fā)布的tid落在同一個庫上,會不會出現(xiàn)數(shù)據(jù)不均衡?
答:只要uid是均衡的,每個用戶發(fā)布的平均帖子數(shù)是均衡的,每個庫的數(shù)據(jù)就是均衡的。
潛在問題二:最開始分16庫,分庫基因是4bit,未來要擴(kuò)充成32庫,分庫基因變成了5bit,那怎么辦?
答:需要提前做好容量預(yù)估,例如事先規(guī)劃好5年內(nèi)數(shù)據(jù)增長256庫足夠,就提前預(yù)留8bit基因。
七、總結(jié)
將以“帖子中心”為典型的“1對多”類業(yè)務(wù),在架構(gòu)上,采用元數(shù)據(jù)與索引數(shù)據(jù)分離的架構(gòu)設(shè)計(jì)方法:
- 帖子服務(wù),元數(shù)據(jù)滿足uid和tid的查詢需求
- 搜索服務(wù),索引數(shù)據(jù)滿足復(fù)雜搜索尋求
對于元數(shù)據(jù)的存儲,在數(shù)據(jù)量較大的情況下,有三種常見的切分方法:
- tid切分法,按照tid分庫,同一個用戶發(fā)布的帖子落在不同的庫上,通過uid來查詢要遍歷所有庫
- uid切分法,按照uid分庫,同一個用戶發(fā)布的帖子落在同一個庫上,需要通過索引表或者緩存來記錄tid與uid的映射關(guān)系,通過tid來查詢時,先查到uid,再通過uid定位庫
- 基因法,按照uid分庫,在生成tid里加入uid上的分庫基因,保證通過uid和tid都能直接定位到庫
對于1對多的業(yè)務(wù)場景,分庫架構(gòu)不再是瓶頸。
【本文為專欄作者“58沈劍”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】
新聞名稱:1對多業(yè)務(wù),數(shù)據(jù)庫水平切分架構(gòu)一次搞定
文章轉(zhuǎn)載:http://m.fisionsoft.com.cn/article/dpdhioc.html


咨詢
建站咨詢
