MTDDL 美团分布式数据访问中间件(转)

原文地址: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配置动态化
动态化配置

MTDDL 美团分布式数据访问中间件(转)的更多相关文章

  1. hibernate 入门([数据访问中间件] 开源框架)

    1.内容:  hibernate 也是一个经典的[数据访问中间件] 开源框架.    2.hibernate核心组件       SessionFactory[整个数据的操作]重量级组件       ...

  2. 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD)

    一些小的C/S项目(winform.WPF等),因需要访问操作数据库,但又不能把DB连接配置在客户端上,原因有很多,可能是DB连接无法直接访问,或客户端不想安装各种DB访问组件,或DB连接不想暴露在客 ...

  3. 适用于app.config与web.config的ConfigUtil读写工具类 基于MongoDb官方C#驱动封装MongoDbCsharpHelper类(CRUD类) 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD) C# 实现AOP 的几种常见方式

    适用于app.config与web.config的ConfigUtil读写工具类   之前文章:<两种读写配置文件的方案(app.config与web.config通用)>,现在重新整理一 ...

  4. .netcore实现一个读写分离的数据库访问中间件

    在实际业务系统中,当单个数据库不能承载负载压力的时候,一般我们采用数据库读写分离的方式来分担数据库负载.主库承担写以及事务操作,从库承担读操作. 为了支持多种数据库我们先定义一个数据类型字典.key为 ...

  5. 美团点评MySQL数据库高可用架构从MMM到MHA+Zebra以及MHA+Proxy的演进

    本文介绍最近几年美团点评MySQL数据库高可用架构的演进过程,以及我们在开源技术基础上做的一些创新.同时,也和业界其它方案进行综合对比,了解业界在高可用方面的进展,和未来我们的一些规划和展望. MMM ...

  6. 《大型网站系统与JAVA中间件实践》读书笔记-大型网站架构演进

    大型网站架构演进 大型网站是一种很常见的分布式系统,除了海量数据和高并发的访问量,本身业务和系统也复杂. 大型网站的架构演进 我们现在常用的大型网站都是从小网站一步一步发展起来的,这个过程中会 有一些 ...

  7. MySQL 中间件汇总比较

    360 Atlas 较为活跃,Atlas 是由 360 Web平台部基础架构团队开发维护的一个基于 MySQL 协议的数据中间层项目.它是在mysql-proxy 0.8.2版本的基础上,对其进行了优 ...

  8. 1.1 DAL数据访问层

    分布式(Distributed)数据访问层(Data Access Layer),简称DAL,是利用MySQL Proxy.Memcached.集群等技术优点而构建的一个架构系统.主要目的是解决高并发 ...

  9. MySQL中间件介绍

    360 Atlas Atlas是由 Qihoo 360, Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目.它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化 ...

随机推荐

  1. springboot打印SQL及多环境配置

    1-在控制台打印sql语句 在springBoot+Mybatis中,要想在控制台日志显示SQL的执行情况,简单设置即可:在properties新增: logging.level.com.anson. ...

  2. Docker简介与安装

    简介与安装 简介 Docker是什么 一款产品从开发到上线,从操作系统,到运行环境,再到应用配置.作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的 ...

  3. Linux-Ubuntu学习笔记

    因学习Python需求,特开此贴用于记录Linux-Ubuntu操作系统的学习笔记. Linux命令-基础版 Linux命令-高级版 此贴终结了,主要用于开发过程中忘记命令时使用.

  4. 接口访问报错:The valid characters are defined in RFC 7230 and RFC 3986

    写了个接口,在测试访问的时候,需要传json串,但是后台报错了 The valid characters are defined in RFC 7230 and RFC 3986 当前使用的tomca ...

  5. Redis 中的数据库

    前面我们花了很多的时间介绍了 redis 中基本的数据结构,及其内部的实现情况,这些都是非常基础的东西,可能不经意间你就会用到他们,希望你花点时间了解一下. 接下来,我们将走近 redis 数据库,学 ...

  6. Internet History,Technology,and Security - History Through Supercomputing(Week2)

    时间飞逝,一周又过去了,这周我们来到了Internet History, Technology and Security (Week 2)的学习,从标题就可以看出,这周主要是介绍“互联网”雏形的诞生. ...

  7. Redux和Context对比

    Redux和Context对比 如果项目体量较小,只是需要一个公共的store存储state,而不讲究使用action来管理state,那context完全可以胜任.反之,则是redux的优点. co ...

  8. BZOJ 3112 [Zjoi2013]防守战线

    题解:单纯形:转化为对偶问题: 对于最大化 cx,满足约束 Ax<=b ,x>0 对偶问题为 最小化 bx,满足约束 ATx>=c ,x>0 (AT为A的转置) 这一题的内存真 ...

  9. Round-number

    Description Most of the time when rounding a given number, it is customary to round to some multiple ...

  10. python做单因素方差分析

    方差分析的主要功能就是验证两组样本,或者两组以上的样本均值是否有显著性差异,即均值是否一样. 这里有两个大点需要注意:①方差分析的原假设是:样本不存在显著性差异(即,均值完全相等):②两样本数据无交互 ...