MTDDL 美團分散式數據訪問中間件(轉) "原文地址:MTDDL——美團點評分散式數據訪問層中間件" 因原文文字和圖顯示有問題,故整理於此,僅供參考。 業界方案 | 組件 | 簡介 | | : : | : | | Atlas | Qihoo 360開發維護的一個基於MySQL協議的數據中間層項目。 ...
MTDDL 美團分散式數據訪問中間件(轉)
因原文文字和圖顯示有問題,故整理於此,僅供參考。
業界方案
組件 | 簡介 |
---|---|
Atlas | Qihoo 360開發維護的一個基於MySQL協議的數據中間層項目。它實現了MySQL的客戶端與服務端協議,作為服務端與應用程式通信,同時作為客戶端與MySQL通信。 |
Cobar | 阿裡巴巴B2B開發的關係型分散式系統,管理將近3000個MySQL實例。 在阿裡經受住了考驗,後面由於作者的走開的原因cobar沒有人維護 了,阿裡也開發了tddl替代cobar。 |
MyCAT | 社區愛好者在阿裡cobar基礎上進行二次開發,解決了cobar當時存 在的一些問題,並且加入了許多新的功能在其中。目前MyCAT社區活 躍度很高,目前已經有一些公司在使用MyCAT。 |
TDDL | 淘寶根據自己的業務特點開發了TDDL(Taobao Distributed Data Layer 框架,主要解決了分庫分表對應用的透明化以及異構資料庫之間的數據複製,它是一個基於集中式配置的 jdbc datasource實現,具有主備,讀寫分離,動態資料庫配置等功能。 |
Zebra | 點評集團統一使用的MySQL資料庫訪問層的中間件。主要提供對業務開發透明、讀寫分庫、分庫分表能力,並提供了端到端SQL監控的集成方案 |
MTDDL(Meituan Distributed Data Layer),美團點評分散式數據訪問層中間件,旨在為全公司提供一個通用數據訪問層服務,支持MySQL動態數據源、讀寫分離、分散式唯一主鍵生成器、分庫分表、動態化配置等功能,並且支持從客戶端角度對數據源的各方面(比如連接池、SQL等)進行監控,後續考慮支持NoSQL、Cache等多種數據源。
- 動態數據源
- 讀寫分離
- 分散式唯一主鍵生成器
- 分庫分表
- 連接池及SQL監控
- 動態化配置
下圖是一次完整的DAO層insert方法調用時序圖,簡單闡述了MTDDL的整個邏輯架構。其中包含了分散式唯一主鍵的獲取、動態數據源的路由以及SQL埋點監控等過程:
動態數據源及讀寫分離
在Spring JDBC AbstractRoutingDataSource的基礎上擴展出MultipleDataSource動態數據源類,通過動態數據源註解及AOP實現。
動態數據源
MultipleDataSource動態數據源類,繼承於Spring JDBC AbstractRoutingDataSource抽象類,實現了determineCurrentLookupKey方法,通過setDataSourceKey方法來動態調整dataSourceKey,進而達到動態調整數據源的功能。其類圖如下:
動態數據源AOP
ShardMultipleDataSourceAspect動態數據源切麵類,針對DAO方法進行功能增強,通過掃描DataSource動態數據源註解來獲取相應的dataSourceKey,從而指定具體的數據源。具體流程圖如下:
配置和使用方式舉例
/**配置參考*/
<bean id="multipleDataSource" class="com.sankuai.meituan.waimai.datasource.multi.MultipleDataSource">
/** 數據源配置 */
<property name="targetDataSources">
<map key-type="java.lang.String">
/** 寫數據源 */
<entry key="dbProductWrite" value-ref="dbProductWrite"/>
/** 讀數據源 */
<entry key="dbProductRead" value-ref="dbProductRead"/>
</map>
</property>
</bean>
/**
* DAO使用動態數據源註解
*/
public interface WmProductSkuDao {
/** 增刪改走寫數據源 */
@DataSource("dbProductWrite")
public void insert(WmProductSku sku);
/** 查詢走讀數據源 */
@DataSource("dbProductRead")
public void getById(long sku_id);
}
分散式唯一主鍵生成器
眾所周知,分庫分表首先要解決的就是分散式唯一主鍵的問題,業界也有很多相關方案:
序號 | 實現方案 | 優點 | 缺點 |
---|---|---|---|
1 | UUID | 本地生成,不需要RPC,低延時,擴展性好,基本沒有性能上限 | 無法保證趨勢遞增,UUID過長128位,不易存儲,往往用字元串表示 |
2 | Snowflake或MongoDB ObjectId | 分散式生成,無單點;趨勢遞增,生成效率快 | 沒有全局時鐘的情況下,只能保證趨勢遞增;當通過NTP進行時鐘同步時可能會出現重覆ID;數據間隙較大 |
3 | proxy服務 + 資料庫分段獲取ID | 分散式生成,段用完後需要去DB獲取,同server有序 | 可能產生數據空洞,即有些ID沒有分配就被跳過了,主要原因是在服務重啟的時候發生;無法保證有序,需要未來解決,可能會通過其他介面方案實現 |
綜上,方案3的缺點可以通過一些手段避免,但其他方案的缺點不好處理,所以選擇第3種方案。目前該方案已由美團點評技術工程部實現——分散式ID生成系統Leaf,MTDDL集成了此功能。
分散式ID生成系統Leaf
美團點評分散式ID生成系統Leaf,其實是一種基於DB的Ticket服務,通過一張通用的Ticket表來實現分散式ID的持久化,執行update更新語句來獲取一批Ticket,這些獲取到的Ticket會在記憶體中進行分配,分配完之後再從DB獲取下一批Ticket。整體架構圖如下:
每個業務tag對應一條DB記錄,DB MaxID欄位記錄當前該Tag已分配出去的最大ID值。
IDGenerator服務啟動之初向DB申請一個號段,傳入號段長度如 genStep = 10000,DB事務置 MaxID = MaxID + genStep,DB設置成功代表號段分配成功。每次IDGenerator號段分配都通過原子加的方式,待分配完畢後重新申請新號段。
唯一主鍵生成演算法擴展
MTDDL不僅集成了Leaf演算法,還支持唯一主鍵演算法的擴展,通過新增唯一主鍵生成策略類實現IDGenStrategy介面即可。IDGenStrategy介面包含兩個方法:getIDGenType用來指定唯一主鍵生成策略,getId用來實現具體的唯一主鍵生成演算法。其類圖如下:
分庫分表
在動態數據源AOP的基礎上擴展出分庫分表AOP,通過分庫分表ShardHandle類實現分庫分表數據源路由及分表計算。ShardHandle關聯了分庫分表上下文ShardContext類,而ShardContext封裝了所有的分庫分表演算法。其類圖如下:
分庫分表流程圖如下:
分庫分表取模演算法
分庫分表目前預設使用的是取模演算法,分表演算法為 (#shard_key % (group_shard_num * table_shard_num)),分庫演算法為 (#shard_key % (group_shard_num * table_shard_num)) / table_shard_num,其中group_shard_num為分庫個數,table_shard_num為每個庫的分表個數。
例如把一張大表分成100張小表然後散到2個庫,則0-49落在第一個庫、50-99落在第二個庫。核心實現如下:
public class ModStrategyHandle implements ShardStrategy {
@Override
public String getShardType() {
return "mod";
}
@Override
public DataTableName handle(String tableName, String dataSourceKey, int tableShardNum,
int dbShardNum, Object shardValue) {
/** 計算散到表的值 */
long shard_value = Long.valueOf(shardValue.toString());
long tablePosition = shard_value % tableShardNum;
long dbPosition = tablePosition / (tableShardNum / dbShardNum);
String finalTableName = new StringBuilder().append(tableName).append("_").append(tablePosition).toString();
String finalDataSourceKey = new StringBuilder().append(dataSourceKey).append(dbPosition).toString();
return new DataTableName(finalTableName, finalDataSourceKey);
}
}
分庫分表演算法擴展
MTDDL不僅支持分庫分表取模演算法,還支持分庫分表演算法的擴展,通過新增分庫分表策略類實現ShardStrategy介面即可。ShardStrategy介面包含兩個方法:getShardType用來指定分庫分表策略,handle用來實現具體的數據源及分表計算邏輯。其類圖如下:
全註解方式接入
為了儘可能地方便業務方接入,MTDDL採用全註解方式使用分庫分表功能,通過ShardInfo、ShardOn、IDGen三個註解實現。
ShardInfo註解用來指定具體的分庫分表配置:包括分表名首碼tableName、分表數量tableShardNum、分庫數量dbShardNum、分庫分表策略shardType、唯一鍵生成策略idGenType、唯一鍵業務方標識idGenKey;ShardOn註解用來指定分庫分表欄位;IDGen註解用來指定唯一鍵欄位。具體類圖如下:
配置和使用方式舉例
// 動態數據源
@DataSource("dbProductSku")
// tableName:分表名首碼,tableShardNum:分表數量,dbShardNum:分庫數量,shardType:分庫分表策略,idGenType:唯一鍵生成策略,idGenKey:唯一鍵業務方標識
@ShardInfo(tableName="wm_food", tableShardNum=100, dbShardNum=1, shardType="mod", idGenType=IDGenType.LEAF, idGenKey=LeafKey.SKU)
@Component
public interface WmProductSkuShardDao {
// @ShardOn("wm_poi_id") 將該註解修飾的對象的wm_poi_id欄位作為shardValue
// @IDGen("id") 指定要設置唯一鍵的欄位
public void insert(@ShardOn("wm_poi_id") @IDGen("id") WmProductSku sku);
// @ShardOn 將該註解修飾的參數作為shardValue
public List<WmProductSku> getSkusByWmPoiId(@ShardOn long wm_poi_id);
}
連接池及SQL監控
DB連接池使用不合理容易引發很多問題,如連接池最大連接數設置過小導致線程獲取不到連接、獲取連接等待時間設置過大導致很多線程掛起、空閑連接回收器運行周期過長導致空閑連接回收不及時等等,如果缺乏有效準確的監控,會造成無法快速定位問題以及追溯歷史。
再者,如果缺乏SQL執行情況相關監控,會很難及時發現DB慢查詢等潛在風險,而慢查詢往往就是DB服務端性能惡化乃至宕機的根源(關於慢查詢,推薦閱讀《MySQL索引原理及慢查詢優化》一文)。MTDDL從1.0.2版本開始正式引入連接池及SQL監控等相關功能。
連接池監控
實現方案
結合Spring完美適配c3p0、dbcp1、dbcp2、mtthrift等多種方案,自動發現新加入到Spring容器中的數據源進行監控,通過美團點評統一監控組件JMonitor上報監控數據。整體架構圖如下:
連接數量監控
監控連接池active、idle、total連接數量,Counter格式:(連接池類型.數據源.active/idle/total_connection),效果圖如下:
獲取連接時間監控
監控獲取空閑連接時間,Counter格式:(ds.getConnection.數據源.time),效果圖如下:
SQL監控
實現方案
採用Spring AOP技術對所有DAO方法進行功能增強處理,通過美團點評分散式會話跟蹤組件MTrace進行SQL調用數據埋點及上報,進而實現從客戶端角度對SQL執行耗時、QPS、調用量、超時率、失敗率等指標進行監控。整體架構圖如下:
實現效果
登錄美團點評的服務治理平臺OCTO選擇服務查看去向分析,效果圖如下:
動態化配置
為了滿足業務方一些動態化需求,如解決線上DB緊急事故需動態調整數據源或者分庫分表相關配置,要求無需重啟線上修改立即生效,MTDDL從1.0.3版本開始正式引入動態化配置相關功能。
實現方案
在Spring容器啟動的時候自動註冊數據源及分庫分表相關配置到美團點評的統一配置中心MCC,在MCC配置管理頁面可以進行動態調整,MCC客戶端在感知到變更事件後會刷新本地配置,如果是數據源配置變更會根據新的配置構造出一個新數據源來替換老數據源,最後再將老的數據源優雅關閉掉。具體流程圖如下:
動態化數據源
目前支持dbcp、dbcp2、c3p0等數據源,效果圖如下:
分庫分表動態化
支持動態化配置分庫分表數量、分庫分表策略、唯一鍵生成策略、唯一鍵業務方標識等,效果圖如下:
MTDDL到目前為止總共開發了四期,後續考慮逐步開源,具體版本迭代如下:
項目名 | 功能 | 開始時間 | 結束時間 | 正式版本 | 快照版本 | 版本備註 |
---|---|---|---|---|---|---|
MTDDL一期 | 動態數據源 | 2016.05.30 | 2016.06.16 | 0.0.1 | 0.0.1-SNAPSHOT | MTDDL第一版 |
讀寫分離 | ||||||
分散式唯一主鍵生成器 | ||||||
分庫分表 | ||||||
MTDDL二期 | 分散式唯一主鍵生成演算法可擴展 | 2016.08.23 | 2016.09.05 | 1.0.1 | 1.0.1-SNAPSHOT | MTDDL接入優化 |
支持零配置接入MTDDL | ||||||
優化shardkey配置方式 | ||||||
MTDDL三期 | 連接池及SQL監控 | 2016.09.06 | 2016.09.20 | 1.0.2 | 1.0.2-SNAPSHOT | MTDDL監控完善 |
緩存優化 | ||||||
MTDDL四期 | 唯一主鍵生成註解化 | 2016.10.11 | 2016.11.08 | 1.0.3 | 1.0.3-SNAPSHOT | MTDDL配置動態化 |
動態化配置 |