轉發自:web
數據庫水平切分是一個頗有意思的話題,不一樣業務類型,數據庫水平切分的方法不一樣。算法
本篇將以「訂單中心」爲例,介紹「多key」類業務,隨着數據量的逐步增大,數據庫性能顯著下降,數據庫水平切分相關的架構實踐。數據庫
1、什麼是「多key」類業務架構
所謂的「多key」,是指一條元數據中,有多個屬性上存在前臺在線查詢需求。併發
訂單中心業務分析異步
訂單中心是一個很是常見的「多key」業務,主要提供訂單的查詢與修改的服務,其核心元數據爲:數據庫設計
Order(oid, buyer_uid, seller_uid, time,money, detail…);分佈式
其中:post
oid爲訂單ID,主鍵性能
buyer_uid爲買家uid
seller_uid爲賣家uid
time, money, detail, …等爲訂單屬性
數據庫設計上,通常來講在業務初期,單庫單表就可以搞定這個需求,典型的架構設計爲:
order-center:訂單中心服務,對調用者提供友好的RPC接口
order-db:對訂單進行數據存儲
隨着訂單量的愈來愈大,數據庫須要進行水平切分,因爲存在多個key上的查詢需求,用哪一個字段進行切分,成了須要解決的關鍵技術問題:
若是用oid來切分,buyer_uid和seller_uid上的查詢則須要遍歷多庫
若是用buyer_uid或seller_uid來切分,其餘屬性上的查詢則須要遍歷多庫
總之,很難有一個徹底之策,在展開技術方案以前,先一塊兒梳理梳理查詢需求。
2、訂單中心屬性查詢需求分析
在進行架構討論以前,先來對業務進行簡要分析,看哪些屬性上有查詢需求。
前臺訪問,最典型的有三類需求:
訂單實體查詢:經過oid查詢訂單實體,90%流量屬於這類需求
用戶訂單列表查詢:經過buyer_uid分頁查詢用戶歷史訂單列表,9%流量屬於這類需求
商家訂單列表查詢:經過seller_uid分頁查詢商家歷史訂單列表,1%流量屬於這類需求
前臺訪問的特色:吞吐量大,服務要求高可用,用戶對訂單的訪問一致性要求高,商家對訂單的訪問一致性要求相對較低,能夠接受必定時間的延時。
後臺訪問,根據產品、運營需求,訪問模式各異:
按照時間,架構,商品,詳情來進行查詢
後臺訪問的特色:運營側的查詢基本上是批量分頁的查詢,因爲是內部系統,訪問量很低,對可用性的要求不高,對一致性的要求也沒這麼嚴格,容許秒級甚至十秒級別的查詢延時。
這兩類不一樣的業務需求,應該使用什麼樣的架構方案來解決呢?
3、前臺與後臺分離的架構設計
若是前臺業務和後臺業務公用一批服務和一個數據庫,有可能致使,因爲後臺的「少數幾個請求」的「批量查詢」的「低效」訪問,致使數據庫的cpu偶爾瞬時100%,影響前臺正經常使用戶的訪問(例如,訂單查詢超時)。
前臺與後臺訪問的查詢需求不一樣,對系統的要求也不同,故應該二者解耦,實施「前臺與後臺分離」的架構設計。
前臺業務架構不變,站點訪問,服務分層,數據庫水平切分。
後臺業務需求則抽取獨立的web/service/db來支持,解除系統之間的耦合,對於「業務複雜」「併發量低」「無需高可用」「能接受必定延時」的後臺業務:
能夠去掉service層,在運營後臺web層經過dao直接訪問數據層
能夠不須要反向代理,不須要集羣冗餘
能夠經過MQ或者線下異步同步數據,犧牲一些數據的實時性
可使用更契合大量數據容許接受更高延時的「索引外置」或者「HIVE」的設計方案
解決了後臺業務的訪問需求,問題轉化爲,前臺的oid,buyer_uid,seller_uid如何來進行數據庫水平切分呢?
多個維度的查詢較爲複雜,對於複雜系統設計,能夠逐步簡化。
4、假設沒有seller_uid
訂單中心,假設沒有seller_uid上的查詢需求,而只有oid和buyer_uid上的查詢需求,就蛻化爲一個「1對多」的業務場景,對於「1對多」的業務,水平切分應該使用「基因法」。
再次回顧一下,什麼是分庫基因?
經過buyer_uid分庫,假設分爲16個庫,採用buyer_uid%16的方式來進行數據庫路由,所謂的模16,其本質是buyer_uid的最後4個bit決定這行數據落在哪一個庫上,這4個bit,就是分庫基因。
也再次回顧一下,什麼是基因法分庫?
在訂單數據oid生成時,oid末端加入分庫基因,讓同一個buyer_uid下的全部訂單都含有相同基因,落在同一個分庫上。
如上圖所示,buyer_uid=666的用戶下了一個訂單:
使用buyer_uid%16分庫,決定這行數據要插入到哪一個庫中
分庫基因是buyer_uid的最後4個bit,即1010
在生成訂單標識oid時,先使用一種分佈式ID生成算法生成前60bit(上圖中綠色部分)
將分庫基因加入到oid的最後4個bit(上圖中粉色部分),拼裝成最終64bit的訂單oid(上圖中藍色部分)
經過這種方法保證,同一個用戶下的全部訂單oid,都落在同一個庫上,oid的最後4個bit都相同,因而:
經過buyer_uid%16可以定位到庫
經過oid%16也能定位到庫
5、假設沒有oid
訂單中心,假設沒有oid上的查詢需求,而只有buyer_uid和seller_uid上的查詢需求,就蛻化爲一個「多對多」的業務場景,對於「多對多」的業務,水平切分應該使用「數據冗餘法」。
如上圖所示:
當有訂單生成時,經過buyer_uid分庫,oid中融入分庫基因,寫入DB-buyer庫
經過線下異步的方式,經過binlog+canal,將數據冗餘到DB-seller庫中
buyer庫經過buyer_uid分庫,seller庫經過seller_uid分庫,前者知足oid和buyer_uid的查詢需求,後者知足seller_uid的查詢需求
數據冗餘的方法有不少種:
服務同步雙寫
服務異步雙寫
線下異步雙寫(上圖所示,是線下異步雙寫)
無論哪一種方案,由於兩步操做不能保證原子性,總有出現數據不一致的可能,高吞吐分佈式事務是業內還沒有解決的難題,此時的架構優化方向,並非徹底保證數據的一致,而是儘早的發現不一致,並修復不一致。
最終一致性,是高吞吐互聯網業務一致性的經常使用實踐。保證數據最終一致性的方案有三種:
冗餘數據全量定時掃描
冗餘數據增量日誌掃描
冗餘數據線上消息實時檢測
這些方案細節在「多對多」業務水平拆分的文章裏詳細展開分析過,便再也不贅述。
6、oid/buyer_uid/seller_uid同時存在
經過上述分析:
若是沒有seller_uid,「多key」業務會蛻化爲「1對多」業務,此時應該使用「基因法」分庫:使用buyer_uid分庫,在oid中加入分庫基因
若是沒有oid,「多key」業務會蛻化爲「多對多」業務,此時應該使用「數據冗餘法」分庫:使用buyer_uid和seller_uid來分別分庫,冗餘數據,知足不一樣屬性上的查詢需求
若是oid/buyer_uid/seller_uid同時存在,可使用上述兩種方案的綜合方案,來解決「多key」業務的數據庫水平切分難題
7、總結
任何複雜難題的解決,都是一個化繁爲簡,逐步擊破的過程。
對於像訂單中心同樣複雜的「多key」類業務,在數據量較大,須要對數據庫進行水平切分時,對於後臺需求,採用「前臺與後臺分離」的架構設計方法:
前臺、後臺系統web/service/db分離解耦,避免後臺低效查詢引起前臺查詢抖動
採用前臺與後臺數據冗餘的設計方式,分別知足兩側的需求
採用「外置索引」(例如ES搜索系統)或者「大數據處理」(例如HIVE)來知足後臺變態的查詢需求
對於前臺需求,化繁爲簡的設計思路,將「多key」類業務,分解爲「1對多」類業務和「多對多」類業務分別解決:
使用「基因法」,解決「1對多」分庫需求:使用buyer_uid分庫,在oid中加入分庫基因,同時知足oid和buyer_uid上的查詢需求
使用「數據冗餘法」,解決「多對多」分庫需求:使用buyer_uid和seller_uid來分別分庫,冗餘數據,知足buyer_uid和seller_uid上的查詢需求
若是oid/buyer_uid/seller_uid同時存在,能夠使用上述兩種方案的綜合方案,來解決「多key」業務的數據庫水平切分難題。
數據冗餘會帶來一致性問題,高吞吐互聯網業務,要想徹底保證事務一致性很難,常見的實踐是最終一致性。
任何脫離業務的架構設計都是耍流氓,共勉。