訂單中心,1億資料架構,這次服了

58沈劍發表於2020-08-27

訂單中心,是網際網路業務中,一個典型的“多key”業務,即:使用者ID,商家ID,訂單ID等多個key上都有業務查詢需求。

隨著資料量的逐步增大,併發量的逐步增大,訂單中心這種“多key”業務,架構應該如何設計,有哪些因素需要考慮,是本文將要系統性討論的問題。

什麼是“多key”類業務?
所謂的“多key”,是指一條後設資料中,有多個屬性上存在前臺線上查詢需求。

訂單中心是什麼業務,有什麼典型業務需求?
訂單中心是一個非常常見的“多key”業務,主要提供訂單的查詢與修改的服務,其核心後設資料為:
Order(oid, buyer_uid, seller_uid, time, money, detail…);
其中:
(1)oid為訂單ID,主鍵;
(2)buyer_uid為買家uid;
(3)seller_uid為賣家uid;
(4)time, money, detail, …等為訂單屬性;

資料庫設計上,一般來說在業務初期,單庫,配合查詢欄位上的索引,就能滿足後設資料儲存與查詢需求。
訂單中心,1億資料架構,這次服了
(1)order-center:訂單中心服務,對呼叫者提供友好的RPC介面;
(2)order-db:對訂單進行資料儲存,並在訂單,買家,賣家等欄位建立索引;

隨著訂單量的越來越大,資料庫需要進行水平切分,由於存在多個key上的查詢需求,用哪個欄位進行切分呢?
(1)如果用oid來切分,buyer_uid和seller_uid上的查詢則需要遍歷多庫;
(2)如果用buyer_uid或seller_uid來切分,其他屬性上的查詢則需要遍歷多庫;

總之,很難有一個萬全之策,在展開技術方案之前,先一起梳理梳理查詢需求。

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

訂單中心,典型業務查詢需求有哪些?

第一類,前臺訪問,最典型的有三類需求:
(1)訂單實體查詢:透過oid查詢訂單實體,90%流量屬於這類需求;
(2)使用者訂單列表查詢:透過buyer_uid分頁查詢使用者歷史訂單列表,9%流量屬於這類需求;
(3)商家訂單列表查詢:透過seller_uid分頁查詢商家歷史訂單列表,1%流量屬於這類需求;

前臺訪問的特點是什麼呢?
吞吐量大,服務要求高可用,使用者對訂單的訪問一致性要求高,商家對訂單的訪問一致性要求相對較低,可以接受一定時間的延時。

第二類,後臺訪問,根據產品、運營需求,訪問模式各異:
(1)按照時間,價格,商品,詳情來進行查詢;

後臺訪問的特點是什麼呢?
運營側的查詢基本上是批次分頁的查詢,由於是內部系統,訪問量很低,對可用性的要求不高,對一致性的要求也沒這麼嚴格,允許秒級甚至十秒級別的查詢延時。

這兩類不同的業務需求,應該使用什麼樣的架構方案來解決呢?

要點一:前臺與後臺分離的架構設計。

如果前臺業務和後臺業務共用一批服務和一個資料庫,有可能導致,由於後臺的“少數幾個請求”的“批次查詢”的“低效”訪問,導致資料庫的cpu偶爾瞬時100%,影響前臺正常使用者的訪問(例如,訂單查詢超時)。
訂單中心,1億資料架構,這次服了
前臺與後臺訪問的查詢需求不同,對系統的要求也不一樣,故應該兩者解耦,實施“前臺與後臺分離”的架構設計。

前臺業務架構不變,站點訪問,服務分層,資料庫水平切分。

後臺業務需求則抽取獨立的web/service/db來支援,解除系統之間的耦合,對於“業務複雜”“併發量低”“無需高可用”“能接受一定延時”的後臺業務:
(1)可以去掉service層,在運營後臺web層透過dao直接訪問資料層;
(2)可以不需要反向代理,不需要叢集冗餘;
(3)可以透過MQ或者線下非同步同步資料,犧牲一些資料的實時性;
(4)可以使用更契合大量資料允許接受更高延時的“索引外接”或者“HIVE”的設計方案;

關於前臺與後臺分離的架構設計,在《使用者中心,1億資料架構,這次服了》一文中有更為細緻的分析,便不再展開。

解決完了後臺業務的訪問需求,那前臺的oid,buyer_uid,seller_uid如何來進行資料庫水平切分呢?

要點二:多個維度的查詢較為複雜,對於複雜系統設計,應該逐個擊破。

假設沒有seller_uid,應該如何擊破oid和buyer_uid的查詢需求?
訂單中心,假設只有oid和buyer_uid上的查詢需求,就蛻化為一個“1對多”的業務場景,對於“1對多”的業務,水平切分應該使用“基因法”

要點三:基因法,是解決“1對多”業務,資料庫水平切分的常見方案。

什麼是分庫基因?
透過buyer_uid分庫,假設分為16個庫,採用buyer_uid%16的方式來進行資料庫路由,所謂的模16,其本質是buyer_uid的最後4個bit決定這行資料落在哪個庫上,這4個bit,就是分庫基因。

什麼是基因法分庫?
在訂單資料oid生成時,oid末端加入分庫基因,讓同一個buyer_uid下的所有訂單都含有相同基因,落在同一個分庫上。
訂單中心,1億資料架構,這次服了
如上圖所示,buyer_uid=666的使用者下了一個訂單:
(1)使用buyer_uid%16分庫,決定這行資料要插入到哪個庫中;
(2)分庫基因是buyer_uid的最後4個bit,即1010;
(3)在生成訂單標識oid時,先使用一種分散式ID生成演算法生成前60bit(上圖中綠色部分);
(4)將分庫基因加入到oid的最後4個bit(上圖中粉色部分),拼裝成最終64bit的訂單oid(上圖中藍色部分);

透過這種方法保證,同一個使用者下的所有訂單oid,都落在同一個庫上,oid的最後4個bit都相同,於是:
(1)透過buyer_uid%16能夠定位到庫;
(2)透過oid%16也能定位到庫;

關於“一對多”業務,以及“基因法”,在《帖子中心,1億資料架構,這次服了》一文中有更為細緻的分析,便不再展開。

假設沒有oid,應該如何擊破buyer_uid和seller_uid的查詢需求?
訂單中心,假設只有buyer_uid和seller_uid上的查詢需求,就蛻化為一個“多對多”的業務場景,對於“多對多”的業務,水平切分應該使用“資料冗餘法”
訂單中心,1億資料架構,這次服了
如上圖所示:
(1)當有訂單生成時,透過buyer_uid分庫,oid中融入分庫基因,寫入DB-buyer庫;
(2)透過線下非同步的方式,透過binlog+canal,將資料冗餘到DB-seller庫中;
(3)buyer庫透過buyer_uid分庫,seller庫透過seller_uid分庫,前者滿足oid和buyer_uid的查詢需求,後者滿足seller_uid的查詢需求;

資料冗餘的方法有很多種:
(1)服務同步雙寫;
(2)服務非同步雙寫;
(3)線下非同步雙寫(上圖所示,是線下非同步雙寫);

要點四:資料冗餘,是解決“多對多”業務,資料庫水平切分的常見方案。

不管哪種方案,因為兩步操作不能保證原子性,總有出現資料不一致的可能,高吞吐分散式事務是業內尚未解決的難題,此時的架構方向,是最終一致性,並不是完全保證資料的一致,而是儘早的發現不一致,並修復不一致。

要點五:最終一致性,是高吞吐網際網路業務一致性的常用實踐。

保證冗餘資料最終一致的常見方案有三種:
(1)冗餘資料全量定時掃描;
(2)冗餘資料增量日誌掃描;
(3)冗餘資料線上訊息實時檢測;

關於“多對多”業務,資料冗餘多種方案,資料冗餘保證最終一致性多種方案,在《好友中心,1億資料架構,這次服了》一文中有更為細緻的分析,便不再展開。

那如果oid/buyer_uid/seller_uid同時存在呢?
綜合上面的解決方案即可:
(1)如果沒有seller_uid,“多key”業務會蛻化為“1對多”業務,此時應該使用“基因法”分庫:使用buyer_uid分庫,在oid中加入分庫基因;
(2)如果沒有oid,“多key”業務會蛻化為“多對多”業務,此時應該使用“資料冗餘法”分庫:使用buyer_uid和seller_uid來分別分庫,冗餘資料,滿足不同屬性上的查詢需求;
(3)如果oid/buyer_uid/seller_uid同時存在,可以使用上述兩種方案的綜合方案,來解決“多key”業務的資料庫水平切分難題

要點總結

一:前後臺差異化需求,可使用前臺與後臺分離的架構設計;
二:對於複雜系統設計,應該逐個擊破
三:基因法,是解決“1對多”業務,資料庫水平切分的常見方案;
四:資料冗餘,是解決“多對多”業務,資料庫水平切分的常見方案;
五:最終一致性,是高吞吐網際網路業務一致性的常用實踐。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31556838/viewspace-2715197/,如需轉載,請註明出處,否則將追究法律責任。

相關文章