訂單中心,是網際網路業務中,一個典型的“多key”業務,即:使用者ID,商家ID,訂單ID等多個key上都有業務查詢需求。
隨著資料量的逐步增大,併發量的逐步增大,訂單中心這種“多key”業務,架構應該如何設計,有哪些因素需要考慮,是本文將要系統性討論的問題。
所謂的“多key”,是指一條後設資料中,有多個屬性上存在前臺線上查詢需求。訂單中心是一個非常常見的“多key”業務,主要提供訂單的查詢與修改的服務,其核心後設資料為:Order(oid, buyer_uid, seller_uid, time, money, detail…);(4)time, money, detail, …等為訂單屬性;資料庫設計上,一般來說在業務初期,單庫,配合查詢欄位上的索引,就能滿足後設資料儲存與查詢需求。(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%流量屬於這類需求;吞吐量大,服務要求高可用,使用者對訂單的訪問一致性要求高,商家對訂單的訪問一致性要求相對較低,可以接受一定時間的延時。第二類,後臺訪問,根據產品、運營需求,訪問模式各異:運營側的查詢基本上是批次分頁的查詢,由於是內部系統,訪問量很低,對可用性的要求不高,對一致性的要求也沒這麼嚴格,允許秒級甚至十秒級別的查詢延時。這兩類不同的業務需求,應該使用什麼樣的架構方案來解決呢?如果前臺業務和後臺業務共用一批服務和一個資料庫,有可能導致,由於後臺的“少數幾個請求”的“批次查詢”的“低效”訪問,導致資料庫的cpu偶爾瞬時100%,影響前臺正常使用者的訪問(例如,訂單查詢超時)。前臺與後臺訪問的查詢需求不同,對系統的要求也不一樣,故應該兩者解耦,實施“前臺與後臺分離”的架構設計。前臺業務架構不變,站點訪問,服務分層,資料庫水平切分。後臺業務需求則抽取獨立的web/service/db來支援,解除系統之間的耦合,對於“業務複雜”“併發量低”“無需高可用”“能接受一定延時”的後臺業務:(1)可以去掉service層,在運營後臺web層透過dao直接訪問資料層;(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下的所有訂單都含有相同基因,落在同一個分庫上。如上圖所示,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億資料架構,這次服了》一文中有更為細緻的分析,便不再展開。假設沒有oid,應該如何擊破buyer_uid和seller_uid的查詢需求?訂單中心,假設只有buyer_uid和seller_uid上的查詢需求,就蛻化為一個“多對多”的業務場景,對於“多對多”的業務,水平切分應該使用“資料冗餘法”。(1)當有訂單生成時,透過buyer_uid分庫,oid中融入分庫基因,寫入DB-buyer庫;(2)透過線下非同步的方式,透過binlog+canal,將資料冗餘到DB-seller庫中;(3)buyer庫透過buyer_uid分庫,seller庫透過seller_uid分庫,前者滿足oid和buyer_uid的查詢需求,後者滿足seller_uid的查詢需求;(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/,如需轉載,請註明出處,否則將追究法律責任。