多key業務,數據庫水平切分架構一次搞定

轉發自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」業務的數據庫水平切分難題。

 

數據冗餘會帶來一致性問題,高吞吐互聯網業務,要想徹底保證事務一致性很難,常見的實踐是最終一致性

 

任何脫離業務的架構設計都是耍流氓,共勉。

相關文章
相關標籤/搜索