MTDDL——分布式數(shù)據(jù)訪問層中間件

背景

2016年Q3季度初,在美團(tuán)外賣上單2.0項目上線后,商家和商品數(shù)量急速增長,預(yù)估商品庫的容量和寫峰值QPS會很快遇到巨大壓力。隨之而來也會影響線上服務(wù)的查詢性能、DB(數(shù)據(jù)庫,以下統(tǒng)一稱DB)主從延遲、表變更困難等一系列問題。

要解決上面所說的問題,通常有兩種方案。第一種方案是直接對現(xiàn)有的商品庫進(jìn)行垂直拆分,可以緩解目前寫峰值QPS過大、DB主從延遲的問題。第二種方案是對現(xiàn)有的商品庫大表進(jìn)行分庫分表,從根本上解決現(xiàn)有問題。方案一實施起來周期較短,但只能解決一時之痛,由此可見,分庫分表是必然的。

在確定分庫分表的方案之后,我們調(diào)研了外賣訂單、結(jié)算以及主站等業(yè)務(wù)的分庫分表實現(xiàn)方案,也調(diào)研了業(yè)界很多分庫分表中間件。在綜合考慮性能、穩(wěn)定性及實現(xiàn)成本的前提下,最終決定自主研發(fā)客戶端分庫分表中間件MTDDL來支撐外賣商品分庫分表項目,這也就是MTDDL的由來。

當(dāng)然,在MTDDL的設(shè)計研發(fā)過程中,我們充分考慮了MTDDL的通用性、可擴(kuò)展性、功能的全面性和接入的便利性。到目前為止一共開發(fā)了四期,實現(xiàn)了MySQL動態(tài)數(shù)據(jù)源、讀寫分離、分布式唯一主鍵生成器、分庫分表、連接池及SQL監(jiān)控、動態(tài)化配置等一系列功能,支持分庫分表算法、分布式唯一主鍵生成算法的高可擴(kuò)展性,而且支持全注解的方式接入,業(yè)務(wù)方不需要引入任何配置文件。

下面就部分業(yè)界方案及MTDDL的設(shè)計目標(biāo)詳細(xì)展開下,然后從源碼的角度來剖析下MTDDL的整個邏輯架構(gòu)和具體實現(xiàn)。

業(yè)界調(diào)研

設(shè)計目標(biāo)

MTDDL(Meituan Distributed Data Layer),美團(tuán)點(diǎn)評分布式數(shù)據(jù)訪問層中間件,旨在為全公司提供一個通用數(shù)據(jù)訪問層服務(wù),支持MySQL動態(tài)數(shù)據(jù)源、讀寫分離、分布式唯一主鍵生成器、分庫分表、動態(tài)化配置等功能,并且支持從客戶端角度對數(shù)據(jù)源的各方面(比如連接池、SQL等)進(jìn)行監(jiān)控,后續(xù)考慮支持NoSQL、Cache等多種數(shù)據(jù)源。

功能特性

1.動態(tài)數(shù)據(jù)源

2.讀寫分離

3.分布式唯一主鍵生成器

4.分庫分表

5.連接池及SQL監(jiān)控

6.動態(tài)化配置

邏輯架構(gòu)

下圖是一次完整的DAO層insert方法調(diào)用時序圖,簡單闡述了MTDDL的整個邏輯架構(gòu)。其中包含了分布式唯一主鍵的獲取、動態(tài)數(shù)據(jù)源的路由以及SQL埋點(diǎn)監(jiān)控等過程:

具體實現(xiàn)

動態(tài)數(shù)據(jù)源及讀寫分離

在Spring JDBC AbstractRoutingDataSource的基礎(chǔ)上擴(kuò)展出MultipleDataSource動態(tài)數(shù)據(jù)源類,通過動態(tài)數(shù)據(jù)源注解及AOP實現(xiàn)。

動態(tài)數(shù)據(jù)源

MultipleDataSource動態(tài)數(shù)據(jù)源類,繼承于Spring JDBC AbstractRoutingDataSource抽象類,實現(xiàn)了determineCurrentLookupKey方法,通過setDataSourceKey方法來動態(tài)調(diào)整dataSourceKey,進(jìn)而達(dá)到動態(tài)調(diào)整數(shù)據(jù)源的功能。其類圖如下:

動態(tài)數(shù)據(jù)源AOP

ShardMultipleDataSourceAspect動態(tài)數(shù)據(jù)源切面類,針對DAO方法進(jìn)行功能增強(qiáng),通過掃描DataSource動態(tài)數(shù)據(jù)源注解來獲取相應(yīng)的dataSourceKey,從而指定具體的數(shù)據(jù)源。具體流程圖如下:

配置和使用方式舉例

分布式唯一主鍵生成器

眾所周知,分庫分表首先要解決的就是分布式唯一主鍵的問題,業(yè)界也有很多相關(guān)方案:

綜上,方案3的缺點(diǎn)可以通過一些手段避免,但其他方案的缺點(diǎn)不好處理,所以選擇第3種方案。目前該方案已由美團(tuán)點(diǎn)評技術(shù)工程部實現(xiàn)——分布式ID生成系統(tǒng)Leaf,MTDDL集成了此功能。

分布式ID生成系統(tǒng)Leaf

美團(tuán)點(diǎn)評分布式ID生成系統(tǒng)Leaf,其實是一種基于DB的Ticket服務(wù),通過一張通用的Ticket表來實現(xiàn)分布式ID的持久化,執(zhí)行update更新語句來獲取一批Ticket,這些獲取到的Ticket會在內(nèi)存中進(jìn)行分配,分配完之后再從DB獲取下一批Ticket。整體架構(gòu)圖如下:

每個業(yè)務(wù)tag對應(yīng)一條DB記錄,DB MaxID字段記錄當(dāng)前該Tag已分配出去的最大ID值。

IDGenerator服務(wù)啟動之初向DB申請一個號段,傳入號段長度如 genStep = 10000,DB事務(wù)置 MaxID = MaxID + genStep,DB設(shè)置成功代表號段分配成功。每次IDGenerator號段分配都通過原子加的方式,待分配完畢后重新申請新號段。

唯一主鍵生成算法擴(kuò)展

MTDDL不僅集成了Leaf算法,還支持唯一主鍵算法的擴(kuò)展,通過新增唯一主鍵生成策略類實現(xiàn)IDGenStrategy接口即可。IDGenStrategy接口包含兩個方法:getIDGenType用來指定唯一主鍵生成策略,getId用來實現(xiàn)具體的唯一主鍵生成算法。其類圖如下:

分庫分表

在動態(tài)數(shù)據(jù)源AOP的基礎(chǔ)上擴(kuò)展出分庫分表AOP,通過分庫分表ShardHandle類實現(xiàn)分庫分表數(shù)據(jù)源路由及分表計算。ShardHandle關(guān)聯(lián)了分庫分表上下文ShardContext類,而ShardContext封裝了所有的分庫分表算法。其類圖如下:

分庫分表流程圖如下:

分庫分表取模算法

分庫分表目前默認(rèn)使用的是取模算法,分表算法為 (#shard_key % (group_shard_num * table_shard_num)),分庫算法為 (#shard_key % (group_shard_num * table_shard_num)) / table_shard_num,其中g(shù)roup_shard_num為分庫個數(shù),table_shard_num為每個庫的分表個數(shù)。

例如把一張大表分成100張小表然后散到2個庫,則0-49落在第一個庫、50-99落在第二個庫。核心實現(xiàn)如下:

分庫分表算法擴(kuò)展

MTDDL不僅支持分庫分表取模算法,還支持分庫分表算法的擴(kuò)展,通過新增分庫分表策略類實現(xiàn)ShardStrategy接口即可。ShardStrategy接口包含兩個方法:getShardType用來指定分庫分表策略,handle用來實現(xiàn)具體的數(shù)據(jù)源及分表計算邏輯。其類圖如下:

全注解方式接入

為了盡可能地方便業(yè)務(wù)方接入,MTDDL采用全注解方式使用分庫分表功能,通過ShardInfo、ShardOn、IDGen三個注解實現(xiàn)。

ShardInfo注解用來指定具體的分庫分表配置:包括分表名前綴tableName、分表數(shù)量tableShardNum、分庫數(shù)量dbShardNum、分庫分表策略shardType、唯一鍵生成策略idGenType、唯一鍵業(yè)務(wù)方標(biāo)識idGenKey;ShardOn注解用來指定分庫分表字段;IDGen注解用來指定唯一鍵字段。具體類圖如下:

配置和使用方式舉例

連接池及SQL監(jiān)控

DB連接池使用不合理容易引發(fā)很多問題,如連接池最大連接數(shù)設(shè)置過小導(dǎo)致線程獲取不到連接、獲取連接等待時間設(shè)置過大導(dǎo)致很多線程掛起、空閑連接回收器運(yùn)行周期過長導(dǎo)致空閑連接回收不及時等等,如果缺乏有效準(zhǔn)確的監(jiān)控,會造成無法快速定位問題以及追溯歷史。

再者,如果缺乏SQL執(zhí)行情況相關(guān)監(jiān)控,會很難及時發(fā)現(xiàn)DB慢查詢等潛在風(fēng)險,而慢查詢往往就是DB服務(wù)端性能惡化乃至宕機(jī)的根源(關(guān)于慢查詢,推薦閱讀《MySQL索引原理及慢查詢優(yōu)化》一文)。MTDDL從1.0.2版本開始正式引入連接池及SQL監(jiān)控等相關(guān)功能。

連接池監(jiān)控

實現(xiàn)方案

結(jié)合Spring完美適配c3p0、dbcp1、dbcp2、mtthrift等多種方案,自動發(fā)現(xiàn)新加入到Spring容器中的數(shù)據(jù)源進(jìn)行監(jiān)控,通過美團(tuán)點(diǎn)評統(tǒng)一監(jiān)控組件JMonitor上報監(jiān)控數(shù)據(jù)。整體架構(gòu)圖如下:

連接數(shù)量監(jiān)控

監(jiān)控連接池active、idle、total連接數(shù)量,Counter格式:(連接池類型.數(shù)據(jù)源.active/idle/total_connection),效果圖如下:

獲取連接時間監(jiān)控

監(jiān)控獲取空閑連接時間,Counter格式:(ds.getConnection.數(shù)據(jù)源.time),效果圖如下:

SQL監(jiān)控

實現(xiàn)方案

采用Spring AOP技術(shù)對所有DAO方法進(jìn)行功能增強(qiáng)處理,通過美團(tuán)點(diǎn)評分布式會話跟蹤組件MTrace進(jìn)行SQL調(diào)用數(shù)據(jù)埋點(diǎn)及上報,進(jìn)而實現(xiàn)從客戶端角度對SQL執(zhí)行耗時、QPS、調(diào)用量、超時率、失敗率等指標(biāo)進(jìn)行監(jiān)控。整體架構(gòu)圖如下:

實現(xiàn)效果

登錄美團(tuán)點(diǎn)評的服務(wù)治理平臺OCTO選擇服務(wù)查看去向分析,效果圖如下:

動態(tài)化配置

為了滿足業(yè)務(wù)方一些動態(tài)化需求,如解決線上DB緊急事故需動態(tài)調(diào)整數(shù)據(jù)源或者分庫分表相關(guān)配置,要求無需重啟在線修改立即生效,MTDDL從1.0.3版本開始正式引入動態(tài)化配置相關(guān)功能。

實現(xiàn)方案

在Spring容器啟動的時候自動注冊數(shù)據(jù)源及分庫分表相關(guān)配置到美團(tuán)點(diǎn)評的統(tǒng)一配置中心MCC,在MCC配置管理頁面可以進(jìn)行動態(tài)調(diào)整,MCC客戶端在感知到變更事件后會刷新本地配置,如果是數(shù)據(jù)源配置變更會根據(jù)新的配置構(gòu)造出一個新數(shù)據(jù)源來替換老數(shù)據(jù)源,最后再將老的數(shù)據(jù)源優(yōu)雅關(guān)閉掉。具體流程圖如下:

動態(tài)化數(shù)據(jù)源

目前支持dbcp、dbcp2、c3p0等數(shù)據(jù)源,效果圖如下:

分庫分表動態(tài)化

支持動態(tài)化配置分庫分表數(shù)量、分庫分表策略、唯一鍵生成策略、唯一鍵業(yè)務(wù)方標(biāo)識等,效果圖如下:

版本迭代

MTDDL到目前為止總共開發(fā)了四期,后續(xù)考慮逐步開源,具體版本迭代如下:

美團(tuán)點(diǎn)評技術(shù)博客原文鏈接:http://tech.meituan.com/mtddl.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容