阿里巴巴數據庫分庫分表的實踐

尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️

加入LINE好友

1.阿里巴巴分布式數據層平台發展和演變

業務數據從原來的單庫單表模式變成了數據被拆分到多個數據庫,甚至多個表中,如果在數據訪問層做一下功能的封裝和管控,所有分庫分表的邏輯和數據的跨庫操作都交給應用的開發人員來做到,則對開發人員的要求變得相對高一點,稍有不慎,可能會對平台的業務包括數據帶來較大的影響。

在2006年阿里巴巴B2B團隊以開源方式研發了Cobar這一關係型數據的分布式處理系統。該系統在很大程度上解決了最初使用Oracle數據庫因為存儲數據變得越來越大帶來的擴展性問題,並且為開發人員提供了一個使用相對簡單的用戶體驗,在當時Cobar平均每天處理近50億次的SQL操作。但隨著阿里巴巴業務場景越來越複雜,Cobar平台功能上的約束對滿足某些業務場景顯得力不從心,例如:

1)不支持跨庫情況下的連接、分頁、排序、子查詢操作。

2)SET語句執行會被忽略,處理事務和字符集設置除外。

3)分庫情況下,insert語句必須包含拆分字段列名。

4)分庫情況下,update語句不能更新拆分字段的值。

5)不支持SAVEPOINT操作。

6)使用JDBC時,不支持rewriteBatchedStatements=true參數設置(默認為false)。

7)使用JDBC時,不支持useServerPrepStmts=true參數設置(默認為false)。

8)使用JDBC時,BLOB、BINARY、VARBINARY字段不能使用setBlob()或setBinaryStream()方法設置參數。

2008年阿里巴巴內部基於淘寶業務發展的需要,在Cobar的基礎上重新研發了分布式數據層框架TDDL(Taobao Distributed Data Layer,外號:頭都大了),針對分庫分表場景,提供了對各種業務場景的支持更加完善,開發人員體驗更加友好,管控能力大幅提升。

目前TDDL已經成為阿里巴巴集團內部業務默認使用的分布式數據層中間件,支撐著今天阿里巴巴上千個應用,平均每天SQL調用超千億次。從架構角度(如圖5-3所示),TDDL沿襲了Cobar之前在應用和後端數據庫之間的定位,通過增加對SQL的解析做到了更為精準的路由控制,以及對跨庫join、統計等計算的支持,彌補了之前Cobar在功能上的約束和限制,成為一個完整支持SQL語法兼容的平台。

阿裡巴巴數據庫分庫分表的實踐

圖5-3TDDL架構示意圖

三層數據源每層都按JDBC規範做到,使得對前端應用沒有任何代碼侵入。

Matrix層(TDataSource)做到分庫分表邏輯,底下持有多個GroupDs

實例。

Group層(TGroupDataSource)做到數據庫的主備/讀寫分離邏輯,底下持有多個AtomDs實例。

Atom層(TAtomDataSource)做到數據庫連接(ip、port、password、connec-

tionProperties)等信息的動態推送,持有原子的數據源。

通過TDDL做到一次來自應用的SQL請求,完整的交互流程(如圖5-4所示)中體現了各個服務組件所起到的作用。

阿裡巴巴數據庫分庫分表的實踐

圖5-4TDDL針對一次SQL請求完整處理流程

正是有了這樣的架構和設計,特別是增加了對SQL語義的解析,使得TDDL相比之前的Cobar在功能上提升了一個新的層級,對於Cobar不支持的跨庫數據聚合、子查詢、group by、order by等特性都有了很好的支持,從而成為在分庫分表技術業界被很多技術同仁認可的一套分布式數據層框架,總結來說,TDDL提供了以下優點:

  • 數據庫主備和動態切換。
  • 帶權重的讀寫分離。
  • 單線程讀重試。
  • 集中式數據源信息管理和動態變更。
  • 支持MySQL和Oracle數據庫。
  • 基於JDBC規範,很容易擴展支持做到JDBC規範的數據源。
  • 無Server、client-jar形式存在,應用直連數據庫。
  • 讀寫次數,並發度流程控制,動態變更。
  • 可分析的日志列印,日志流控,動態變更。

隨著阿里巴巴集團業務的多元化,特別是對於除電商領域以外業務的不斷擴展和並購,TDDL這種無Server的模式對於故障的定位和對運行環境的要求(必須是阿里巴巴內部服務環境),支持這些新興業務有了不少困難,所以在2014年,阿里巴巴已經研發出新一代分布式數據庫產品DRDS(Distributed Relational Database Service),該產品相比TDDL在業務場景的支持、故障的定位、運維管控等方面又有了一個全面的提升,今天DRDS已經成為阿里雲上用於解決關係型數據庫線性擴展問題的標準雲產品,服務了幾百家阿里巴巴集團外部的客戶。

2.數據盡可能平均拆分

不管是採用何種分庫分表框架或平台,其核心的思路都是將原本保存在單表中太大的數據進行拆分,將這些數據分散保存到多個數據庫的多個表中,避免因為單表數據太大給數據的訪問帶來讀寫性能的問題。所以在分庫分表場景下,最重要的一個原則就是被拆分的數據盡可能的平均拆分到後端的數據庫中,如果拆分得不均勻,還會產生數據訪問熱點,同樣存在熱點數據因為增長過快而又面臨數據單表數據過大的問題。

而對於數據以什麼樣的維度進行拆分,大家看到很多場景中都是對業務數據的ID(大部分場景此ID是以自增的方式)進行哈希取模的方式將數據進行平均拆分,這個簡單的方式確實在很多場景下都是非常合適的拆分方法,但並不是在所有的場景中這樣拆分的方式都是最優選擇。也就是說數據如何拆分並沒有所謂的金科玉律,更多的是需要結合業務數據的結構和業務場景來決定。

下面以大家最熟悉的電商訂單數據拆分為例,訂單是任何一個電商平台中都會有的業務數據,每個淘寶或天貓用戶在平台上提交訂單後都會在平台後端生成訂單相關的數據,一般記錄一條訂單數據的數據庫表結構如圖5-5所示。

訂單數據主要由三張數據庫表組成,主訂單表對應的就是用戶的一個訂單,每提交一次都會生成一個主訂單表的數據。在有些情況下,用戶可能在一個訂單中選擇不同賣家的商品,而每個賣家又會按照該訂單中是自己提供的商品計算相關的商品優惠(比如滿88元免快遞費)以及安排相關的物流配送,所以會出現子訂單的概念,即一個主訂單會由多個子訂單組成,而真正對應到具體每個商品的訂單信息,則是保存在訂單詳情表中。

阿裡巴巴數據庫分庫分表的實踐

圖5-5訂單相關數據表結構示意

如果一個電商平台的業務發展健康的話,訂單數據是比較容易出現因為單個數據庫表中的數據太大而造成性能的瓶頸,所以需要對它進行數據庫的拆分。此時從理論上對訂單拆分是可以由兩個維度進行的,一個維度是通過訂單ID(一般為自增ID)取模的方式,即以訂單ID為分庫分表鍵;一個是通過買家用戶ID的維度進行哈希取模,即以買家用戶ID為分庫分表鍵。

兩種方案做一下對比:

如果是按照訂單ID取模的方式,比如按64取模,則可以保證主訂單數據以及相關的子訂單、訂單詳情數據平均落入到後端的64個數據庫中,原則上很好地滿足了數據盡可能平均拆分的原則。

通過採用買家用戶ID哈希取模的方式,比如也是按64取模,技術上則也能保證訂單數據拆分到後端的64個數據庫中,但這里就會出現一個業務場景中帶來的一個問題,就是如果有些賣家是交易量非常大的(這樣的群體不在少數),那這些賣家產生的訂單數據量(特別是訂單詳情表的數據量)會比其他賣家要多出不少,也就是會出現數據不平均的現象,最終導致這些賣家的訂單數據所在的數據庫會相對其他數據庫提早進入到數據歸檔(為了避免在線交易數據庫的數據的增大帶來數據庫性能問題,淘寶將3個月內的訂單數據保存進在線交易數據庫中,超過3個月的訂單會歸檔到後端專門的歸檔數據庫)。

所以從對「數據盡可能平均拆分」這條原則來看,按照訂單ID取模的方式看起來是更能保證訂單數據進行平均拆分,但我們暫且不要這麼快下結論,讓我們繼續從下面幾條原則和最佳實踐角度多思考不同的拆分維度帶來的優缺點。

3.盡量減少事務邊界

不管是TDDL平台還是DRDS,採用分庫分表的方式將業務數據拆分後,如果每一條SQL語句中都能帶有分庫分表鍵,如圖5-6所示是以自增的訂單ID以8取模,將訂單平均分布到8個數據庫的訂單表中,通過分布式服務層在對於SQL解析後都能精準地將這條SQL語句推送到該數據所在的數據庫上執行,數據庫將執行的結果再返回給分布式服務層,分布式服務層再將結果返回給應用,整個數據庫訪問的過程跟之前的單機數據庫操作沒有任何差別。這個是在數據進行了分庫分表拆分後,SQL語句執行效率最高的方式。

阿裡巴巴數據庫分庫分表的實踐

圖5-6DRDS對帶分庫分表鍵的SQL請求處理

但不是所有的業務場景在進行數據庫訪問時每次都能帶分庫分表鍵的。比如在買家中心的界面中,要顯示買家test1過去三個月的訂單列表信息,因為該買家test1的訂單按訂單ID取模的方式分布到了不同的數據庫中,此時SQL語句中就沒有了分庫分表鍵值,則出現了如圖5-7所示的情況,分布式數據層會將獲取test1訂單的SQL語句推送到後端所有數據庫中執行,然後將後端數據庫返回的結果在分布式數據層進行聚合後再返回給前端應用。

阿裡巴巴數據庫分庫分表的實踐

圖5-7DRDS對不帶分庫分表鍵的SQL請求進行全表掃描處理

此時就出現了我們所說的全表掃描。此時我們來解釋一下這里「事務邊界」的定義,所謂的事務邊界即是指單個SQL語句在後端數據庫上同時執行的數量,上面示例中就是事務邊界大的典型示例,即一條SQL語句同時被推送到後端所有數據庫中運行。事務邊界的數量越大,會給系統帶來以下弊端:

系統的鎖衝突概率越高。如果事務邊界大的SQL請求比較多,在一次SQL請求處理過程中自然對於後端的數據庫操作的數據庫記錄覆蓋比較廣,當有多個類似的SQL請求並行執行時,則出現數據鎖造成的資源訪問互斥的概率會大大增加。

系統越難以擴展。如果有大量的SQL請求都是這樣全表掃描,或者從極端角度說明這個問題,如果每一次的SQL請求都需要全表掃描執行,你會發現整個平台的數據庫連接數量是取決於後端單個數據庫的連接能力,也就意味著整個數據庫的能力是無法通過增加後端數據庫實例來擴展的。所以如果有大量的全表掃描的SQL請求對於系統的擴展能力會帶來不小的

影響。

整體性能越低。對於性能,這里想強調的是對系統整體性能的影響,而不是單次SQL的性能。應用發送獲取買家test1訂單列表SQL的請求(如

圖5-8步驟①)時,分布式數據層會並行的將這條SQL語句推送(如圖5-8步驟②)到後端8台數據庫上運行,因為訂單數據進行了平均的拆分,單個數據庫訂單表的數據量大小都使得數據庫處於最佳性能表現的狀態,所以意味著每一個數據庫返回的計算結果都是在一個可期望的時間內(比如100毫秒),將結果返回到分布式數據層(如圖5-8步驟③),分布式數據層將從各個數據庫返回來的結果在內存中進行聚合或排序等操作(如圖5-8步驟④),最後返回訂單列表給應用(如圖5-8步驟⑤)。

阿裡巴巴數據庫分庫分表的實踐

圖5-8DRDS對需全表掃描操作的SQL請求處理流程

整個SQL執行的過程包含了5個步驟,仔細看看,你會發現一次帶分庫分表鍵執行的SQL過程也會經歷這5個步驟,區別只是在②③步驟是並行的方式同時跟多個後端數據庫進行交互,但在時間上帶來的影響幾乎是毫秒級的;而第④個步驟是可能造成差異的一個點,如果像示例中一個用戶的訂單信息可能最多幾千條,對於幾千條數據的內存聚合操作,處理時間也是毫秒級的,所以這樣一次全表掃描的操作,用戶的體驗是完全無感知的,跟訪問單機數據庫的體驗是沒有差異的。但如果在第④個步驟中確實遇到對大數據量(比如幾十萬、幾百萬條數據)的聚合、排序、分組等計算時,則會占用較大的內存和CPU計算資源,如果這樣類型的SQL請求比較頻繁的話,就會給分布式數據層帶來較大的資源占用,從而導致整體分布式服務的處理性能受到影響。

很多人對於全表掃描會有一些誤解,甚至認為出現全表掃描對於系統來說是完全不能接受的。其實全表掃描在真實的業務場景中很難完全避免(也可以做到完全避免,但會帶來其他方面的問題,後面會有說明),對於在分布式數據層的內存中進行數據量不大的聚合這類的SQL請求,如果不是高並發同時請求的情況下,比如對訂單進行複雜的條件檢索,如圖5-9所示,就一定需要採用全表掃描的方式,將查詢語句同時推送到後端的數據庫中才能做到該場景的要求,但因為調用不會特別頻繁,而且計算的數據量不會太大,所以整體不會給數據庫整體性能帶來太大的影響。

阿裡巴巴數據庫分庫分表的實踐

圖5-9訂單搜尋是典型的多條件查詢場景

如果是高並發情況下同時請求的話,為了數據庫整體的擴展能力,則要考慮下面描述的異構索引手段來避免這樣的情況發生。對於在內存中要進行大數據量聚合操作和計算的SQL請求,如果這類SQL的不是大量並發或頻繁調用的話,平台本身的性能影響也不會太大,如果這類SQL請求有並發或頻繁訪問的要求,則要考慮採用其他的平台來滿足這一類場景的要求,比如Hadoop這類做大數據量離線分析的產品,如果應用對請求的實時性要求比較高,則可採用如內存數據庫或HBase這類平台,這一部分的內容不在本書中討論。

4.異構索引表盡量降低全表掃描頻率

還是基於訂單數據的分庫分表場景,按照訂單ID取模雖然很好地滿足了訂單數據均勻地保存在後端數據庫中,但在買家查看自己訂單的業務場景中,就出現了全表掃描的情況,而且買家查看自己訂單的請求是非常頻繁的,必然給數據庫帶來擴展或性能的問題,有違「盡量減少事務邊界」這一原則。其實這類場景還有很多,比如賣家要查看與自己店鋪相關的訂單信息,同樣也會出現上述所說的大量進行全表掃描的SQL請求。

針對這類場景問題,最常用的是採用「異構索引表」的方式解決,即採用異步機制將原表內的每一次創建或更新,都換另一個維度保存一份完整的數據表或索引表。本質上這是互聯網公司很多時候都採用的一個解決思路:「拿空間換時間」。

也就是應用在創建或更新一條按照訂單ID為分庫分表鍵的訂單數據時,也會再保存一份按照買家ID為分庫分表鍵的訂單索引數據,如圖5-10所示,其結果就是同一買家的所有訂單索引表都保存在同一數據庫中,這就是給訂單創建了異構索引表。

阿裡巴巴數據庫分庫分表的實踐

圖5-10訂單異構索引表

這時再來看看買家test1在獲取訂單信息進行頁面展現時,應用對於數據庫的訪問流程就發生了如圖的5-11變化。

在有了訂單索引表後,應用首先會通過當前買家ID(以圖示中test1為例),首先到訂單索引表中搜尋出test1的所有訂單索引表(步驟①),因為步驟②SQL請求中帶了以buyer_ID的分庫分表鍵,所以一次是效率最高的單庫訪問,獲取到了買家test1的所有訂單索引表列表並由DRDS返回到了前端應用(步驟③和④),應用在拿到返回的索引列表後,獲取到訂單的ID列表(1,5,8),在發送一次獲取真正訂單列表的請求(步驟⑤),同樣在步驟⑥的SQL語句的條件中帶了分庫分表鍵order_ID的列表值,所以DRDS可以精確地將此SQL請求發送到後端包含in列表值中訂單ID的數據庫,而不會出現全表掃描的情況,最終通過兩次訪問效率最高的SQL請求代替了之前需要進行全表掃描的問題。

阿裡巴巴數據庫分庫分表的實踐

圖5-11基於訂單索引表做到買家訂單列表查看流程示意

這時你可能會指出,為什麼不是將訂單的完整數據按照買家ID維度進行一次分庫保存,這樣就只需要進行一次按買家ID維度進行數據庫的訪問就獲取到訂單的信息?這是一個好問題,其實淘寶的訂單數據就是在異構索引表中全復制的,即訂單按照買家ID維度進行分庫分表的訂單索引表跟以訂單ID維度進行分庫分表的訂單表中的字段完全一樣,這樣確實避免了多一次的數據庫訪問。但一般來說,應用可能會按照多個維度創建多個異構索引表,比如為了避免買家查看自己的訂單時頻繁進行全表掃描,實際中還會以買家ID的維度進行異構索引表的建立,所以採用這樣數據全復制的方法會帶來大量的數據冗餘,從而增加不少數據庫存儲成本。

另外,在某些場景中,在獲取主業務表的列表時,可能需要依賴此業務表所在數據庫的子業務表信息,比如訂單示例中的主、子訂單,因為是以訂單ID的維度進行了分庫分表,所以該訂單相關的子訂單、訂單明細表都會保存在同一個數據庫中,如果我們僅僅是對主訂單信息做了數據全復制的異構保存,還是通過一次對這張異構表的數據進行查詢獲取包含了子訂單信息的訂單列表時,就會出現跨庫join的問題,其對分布式數據層帶來的不良影響其實跟之前所說的全表掃描是一樣的。所以我們還是建議採用僅僅做異構索引表,而不是數據全復制,

同時採用兩次SQL請求的方式解決出現全表掃描的問題。

做到對數據的異步索引創建有多種做到方式,一種是從數據庫層採用數據復制的方式做到;另一種是如圖5-12所示在應用層做到,在這一層做到異構索引數據的創建,就必然會帶來分布式事務的問題。

阿裡巴巴數據庫分庫分表的實踐

圖5-12精衛做到數據同步的流程圖

這里給大家介紹的是在數據庫層做到異構索引的方式,也是阿里巴巴內部目前採用的方式,通過一款名為精衛填海(簡稱精衛)的產品做到了數據的異構復制。本質上精衛是一個基於MySQL的實時數據復制框架,可以通過圖形界面配置的方式就可以做到異構數據復制的需求。除了在同步異構索引數據的場景外,可以認為精衛是一個MySQL的數據觸發器+分發管道。

數據從源數據庫向目標數據庫的過程中,可能需要對數據進行一些過濾和轉換,精衛本身的結構分為抽取器(Extractor)、管道(Pipeline)、分發器(Applier),數據從抽取器流入管道,管道中有過濾器可以執行對數據的一些過濾的操作,然後再交由分發器寫入到目標,如圖5-12所示。

精衛平台通過抽取器(Extractor)獲取到訂單數據創建在MySQL數據庫中產生的binlog日志(binlog日志會記錄對數據發生或潛在發生更改的SQL語句,並以二進制的形式保存在磁盤中),並轉換為event對象,用戶可通過精衛自帶的過濾器(Filter)(比如字段過濾、轉換等)或基於接口自定義開發的過濾器對event對象中的數據進行處理,最終通過分發器(Applier)將結果轉換為發送給DRDS的SQL語句,通過精衛做到異構索引數據的過程如圖5-13所示。

雖然精衛平台在系統設計和提供的功能不算複雜,但其實但凡跟數據相關的平台就不會簡單。這里不會對精衛核心的組件和機制做更詳細的介紹,只是將精衛多年來能力演變後,目前提供的核心功能做一下介紹,為有志在該領域深耕細作的技術同仁多一些思路和借鑒。

阿裡巴巴數據庫分庫分表的實踐

圖5-13採用精衛平台做到異構索引表流程示意

(1)多線程管道做到

在精衛平台應用的早期,數據的同步均是採用單線程管道任務模式,即如

圖5-12中對binlog進行單線程的處理。隨著業務的發展,需要同步的數據量越來越大,單純的單線程管道任務已經成為系統的瓶頸,後來開發了對多線程管道任務的支持(如圖5-14所示)。

阿裡巴巴數據庫分庫分表的實踐

圖5-14精衛支持多線程管道數據同步

但多線程管道就會帶來數據同步的順序問題。在對binlog數據進行多線程並行處理後,就不能保證在源數據庫中執行的SQL語句在目標數據庫的順序一致,這樣在某些業務場景中一定會出現數據不一致性的問題。對於這個問題,目前精衛中提供的解決思路是保證同一條記錄或針對同一分庫表發生的數據同步按照順序執行。

如果最後發送到分布式數據層的SQL語句中沒有分庫鍵,則通過對「庫名+表名+主鍵值」哈希後對線程數取模,這樣就能讓同一條記錄的數據同步事件處理都會在同一線程中順序執行,保證了該記錄多次變更的順序性,但是不保證不同記錄間的順序。如果SQL語句中有分庫鍵,則通過「庫名+分庫鍵值」哈希後對線程數取模,效果是保證不同邏輯表針對相同分庫邏輯的記錄變化順序。

(2)數據的安全

凡是牽涉數據的操作,數據的安全一定是最重要的。如何保證在分布式環境下同步任務效率最大化,同時保證服務的穩定和數據的安全,是很多此類平台精益求精、力求突破的方向。

平台穩定性保障。為了保證同步任務執行的效率最大化,同時互相不會因為資源會搶占或某些同步任務的異常對其他任務造成影響,在精衛的系統設計中,支持多個服務節點作為任務執行的集群,通過統一的任務調度系統(Zookeeper集群),將任務分配到集群中的各節點並行執行。

為了保證任務間不會因為同步任務性能或異常造成互相的干擾,採用了每個同步任務都是獨立Java進程的方式運行,出現異常該任務自動終止。任務調度系統會定期輪詢任務列表,發現任務缺少立即搶占式啟動該任務。

心跳+報警。運行集群與ZooKeeper採用定時心跳的方式,將集群節點的運行狀態以及任務完成的位點(即目前同步任務處理binlog的進度信息)信息同步到Zookeeper上,如果心跳信息異常或位點時間落後過大則立即報警。在抽取器和分發器發生任何錯誤復制任務立即轉變成STANDBY狀態,集群中其他機器上的服務在感知後會立即將自己啟動,繼續執行前一復制任務。

MySQL主備切換。利用比對主備數據庫的狀態信息,通過以下順序,採用手工的方式處理MySQL出現主備切換時進行同步任務的恢復:

1)查看新主庫的當前位點Show master status,獲取到PA狀態。

2)查看老主庫拉去新主庫的位置Show slave status,獲取到PR狀態。

3)如果PR>PA,直接用新主庫的位點PA切換到新主庫上讀取。

如果希望通過自動化的方式,做到的思路則可利用binlog里的serverId和時間戳,發現dump的binlog中的serverId發生變化記錄變化時間戳,然後在給定的MySQL服務器中查找到有同樣變化的數據庫,根據探測到的serverId發生變化的時間戳進行回溯,在新的機器符合條件的位點進行dump。

MySQL異常掛掉。利用數據庫上binlog文件修改時間,按照以下順序採取手工的方式進行整個文件回溯:

1)在數據庫所在的服務器上找到服務掛掉的時間點。

2)到新的主機上查看找到服務掛掉時間點之前最近的binlog文件。

3)從這個文件的位點開始進行回溯。

如果希望通過自動化的方式自動進行恢復,可同樣借鑒MySQL主備切換中提到的自動化做到思路。

(3)友好的用戶自服務接入體驗

精衛平台是整個電商業務做到數據實時同步復制的統一平台,負責來自上千個不同應用的需求,如果每一個應用的接入都需要平台的技術人員給予入門的培訓和支持都是非常大的工作量,也會影響到前端應用的用戶體驗。所以提供一個用戶體驗友好,自帶常用功能的平台,能針對大部分的業務需求可以讓應用方在界面上通過配置的方式就能做到,大大降低接入開發成本。

如圖5-15所示,精衛平台給應用方客戶提供了Web的配置界面,可讓用戶針對需要同步的數據源進行設置,並對數據同步的事件類型(增、刪、改)和是否進行分表以及分庫分表鍵列等進行設置。

精衛平台的數據庫分發器支持一些高級功能,如字段過濾、字段映射、action轉換等,如果自帶功能不滿足需求,可以上傳包含自己的業務邏輯的過濾代碼。這些功能的使用也提供了界面的方式,讓用戶對源數據庫表中的字段如何映射到目標數據庫表進行設置(如圖5-16所示)。

阿裡巴巴數據庫分庫分表的實踐

圖5-15精衛支持界面配置不同數據源間的數據同步

阿裡巴巴數據庫分庫分表的實踐

圖5-16精衛提供的自服務體驗提升數據同步服務接入效率

正是有了這樣簡單易用的用戶體驗,使得精衛平台在應用的接入效率和用戶滿意度上都有非常不錯的表現。

(4)平台管控和統計

在精衛的平台中,每天都運行著上千億次的數據同步和復制任務,必然需要對這些任務的執行有一個清晰的管控,甚至可以從中找出對業務數據變化的趨勢。做到的方法是定時輪詢Zookeeper集群中對應任務的節點進行監控,如圖5-17所示。目前提供以下三個方面監控:

心跳監控。

延遲堆積監控。

任務狀態、數據監控(TPS、異常)等。

阿裡巴巴數據庫分庫分表的實踐

圖5-17精衛平台提供的數據同步監控

採用類似精衛這樣的平台做到數據異構索引的好處是,不需要在各個前端應用層的代碼中去做到,只需統一通過精衛平台做到。有了這樣專業的平台來做到數據同步的效率、服務高可用性、任務管控、統計等,能提供更好的服務。但設計這樣的平台確實需要掌握數據庫相關知識,以及任務調度、平台管控等技術,甚至需要在各種複雜場景中逐步打磨和完善技術。所以如果有些企業還沒有這樣數據同步的專業平台,通常會建議採用通過在應用層做到數據的異構索引,具體做到方式在6.3節中重點闡述。

5.將多條件頻繁查詢引入搜尋引擎平台

採用數據異構索引的方式在實戰中基本能解決和避免90%以上的跨join或全表掃描的情況,是在分布式數據場景下,提升數據庫服務性能和處理吞吐能力的最有效技術手段。但在某些場景下,比如淘寶商品的搜尋(如圖5-18)和高級搜尋(如圖5-19),因為商品搜尋幾乎是訪問淘寶用戶都會進行的操作,所以調用非常頻繁,如果採用SQL語句的方式在商品數據庫進行全表掃描的操作,則必然對數據庫的整體性能和數據庫連接資源帶來巨大的壓力。

阿裡巴巴數據庫分庫分表的實踐

圖5-18淘寶網商品全文搜尋

阿裡巴巴數據庫分庫分表的實踐

圖5-19淘寶網商品高級搜尋

所以面對此類場景,我們不建議採用數據庫的方式提供這樣的搜尋服務,而是採用專業的搜尋引擎平台來行使這樣的職能,做到的架構如圖5-20所示。

阿裡巴巴數據庫分庫分表的實踐

圖5-20全文搜尋做到示意圖

阿里巴巴有自身的主搜尋平台,該平台承載了淘寶、天貓、一淘、1688、什麼搜尋等搜尋業務,其核心功能跟業界開源工具,如Iucene、Solr、ElasticSearch等搜尋引擎類似,但在數據同步(從數據庫到搜尋引擎)、索引創建算法、查詢執行計劃、排序算法等方面針對商品搜尋這樣的場景做了相應的調整和功能增強。該搜尋平台目前已經以阿里雲上OpenSearch產品的形態,給有此類搜尋需求的客戶提供強大的搜尋服務,更多關於該平台詳細的資料可訪問開放搜尋服務的官方網站:https://www.aliyun.com/product/opensearch。

6.簡單就是美

在真實的世界中,選擇的困難往往是因為充滿著各種誘惑,選擇A方案,有這些好處;而選擇B方案,也會有另外一些好處。

如果在「盡量減小事務邊界」與「數據盡可能平均拆分」兩個原則間發生了衝突,那麼請選擇「數據盡可能平均拆分」作為優先考慮原則,因為事務邊界的問題相對來說更好解決,無論是做全表掃描或做異構索引復制都是可以解決的。而寫入或單機容量如果出現不均衡,那麼處理起來難度就比較大。

盡管複雜的切分規則或數據的異構索引能夠給系統的性能和擴展性帶來顯著的收益,但其後面所帶來的系統運維複雜度上升也是不能忽視的一個結果。

如果為每一個存在跨join或全表掃描的場景都採用數據異構索引的方式,整個數據庫出現大量數據冗餘,數據一致性的保障也會帶來挑戰,同時數據庫間的業務邏輯關係也變得非常複雜,給數據庫運維帶來困難和風險,從而對數據庫運維人員的要求和依賴會非常高,所以從系統風險的角度考慮,以82法則,在實際中,我們僅針對那些在80%情況下訪問的那20%的場景進行如數據異構索引這樣的處理,達到這類場景的性能最優化,而對其他80%偶爾出現跨庫join、全表掃描的場景,採用最為簡單直接的方式往往是就最有效的方式。

About 尋夢園
尋夢園是台灣最大的聊天室及交友社群網站。 致力於發展能夠讓會員們彼此互動、盡情分享自我的平台。 擁有數百間不同的聊天室 ,讓您隨時隨地都能找到志同道合的好友!