Big Mysql的中间件
Posted 07H_JH
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Big Mysql的中间件相关的知识,希望对你有一定的参考价值。
淘宝根据自身业务需求研发了TDDL(Taobao Distributed Data Layer)框架,主要用于解决分库分表场景下的访问路由(持久层与数据访问层的配合)以及异构数据库之间的数据同步,它是一个基于集中式配置的JDBC DataSource实现,具有分库分表、Master/Salve、动态数据源配置等功能。就目前而言,许多大厂也在出一些更加优秀和社区支持更广泛的DAL层产品,比如Hibernate Shards、Ibatis-Sharding等。TDDL位于数据库和持久层之间,它直接与数据库建立交道,如图所示:
淘宝很早就对数据进行过分库的处理,上层系统连接多个数据库,中间有一个叫做DBRoute的路由来对数据进行统一访问。DBRoute对数据进行多库的操作、数据的整合,让上层系统像操作一个数据库一样操作多个库。但是随着数据量的增长,对于库表的分法有了更高的要求,例如,你的商品数据到了百亿级别的时候,任何一个库都无法存放了,于是分成2个、4个、8个、16个、32个……直到1024个、2048个。好,分成这么多,数据能够存放了,那怎么查询它?这时候,数据查询的中间件就要能够承担这个重任了,它对上层来说,必须像查询一个数据库一样来查询数据,还要像查询一个数据库一样快(每条查询在几毫秒内完成),TDDL就承担了这样一个工作。在外面有些系统也用DAL(数据访问层) 这个概念来命名这个中间件。下图展示了一个简单的分库分表数据查询策略:
TDDL的主要优点:
- 数据库主备和动态切换
- 带权重的读写分离
- 单线程读重试
- 集中式数据源信息管理和动态变更
- 剥离的稳定jboss数据源
- 支持mysql和oracle数据库
- 基于jdbc规范,很容易扩展支持实现jdbc规范的数据源
- 无server,client-jar形式存在,应用直连数据库
- 读写次数,并发度流程控制,动态变更
- 可分析的日志打印,日志流控,动态变更
TDDL的体系架构
TDDL其实主要可以划分为3层架构,分别是Matrix层、Group层和Atom层。Matrix层用于实现分库分表逻辑,底层持有多个Group实例。而Group层和Atom共同组成了动态数据源, Group层实现了数据库的Master/Salve模式的写分离逻辑,底层持有多个Atom实例。最后Atom层 (TAtomDataSource)实现数据库ip,port,password,connectionProperties等信息的动态推送,以及持有原子的数据源分离的JBOSS数据源)。
持久层只关心对数据源的CRUD操作,而多数据源的访问并不应该由它来关心。也就是说TDDL透明给持久层的数据源接口应该是统一且“单一”的,至于数据库到底如何分库分表持久层无需知道也无需编写对应的SQL去实行应对策略。这个时候对TDDL一些疑问就出现了,TDDL需要对SQL进行二次解析和拼装吗?答案是不解析仅拼装。TDDL只需要从持久层拿到发出的SQL再按照一些分库分表条件,进行特定的SQL扩充以此满足访问路路由操作。
- TDDL除了拿到分库分表条件外,还需要拿到order by、group by、limit、join等信息,SUM、MAX、MIN等聚合函数信息,DISTINCT信息。具有这些关键字的SQL将会在单库和多库情况下进行,语义是不同的。TDDL必须对使用这些关键字的SQL返回的结果做出合适的处理;
- TDDL行复制需要重新拼写SQL,带上sync_version字段;
- 不通过sql解析,因为TDDL遵守JDBC规范,它不可能去扩充JDBC规范里面的接口,所以只能通过SQL中加额外的字符条件(也就是HINT方式)或者ThreadLocal方式进行传递,前者使SQL过长,后者难以维护,开发debug时不容易跟踪,而且需要判定是在一条SQL执行后失效还是1个连接关闭后才失效;
- TDDL现在也同时支持Hint方式和ThreadLocal方式传递这些信息;
前言
在开始讲解淘宝的 TDDL(Taobao Distribute Data Layer) 技术之前,请允许笔者先吐槽一番。首先要开喷的是淘宝的社区支持做的无比的烂, TaoCode 开源社区上面,几乎从来都是有人提问,无人响应。再者版本迭代速度也同样差强人意 , 就目前而言 TDDL 的版本已经全线开源(Group、Atom、Matrix)大家可以在Github上下载源码 。
目录
一、互联网当下的数据库拆分过程
二、 TDDL 的架构原型
三、下载 TDDL 的 Atom 层和 Group 层源代码
四、 Diamond 简介
五、 Diamond 的安装和使用
六、动态数据源层的 Master/Salve 读写分离 配置与实现
七、 Matrix 层的分库分表配置与实现
一、互联网当下的数据库拆分过程
对于一个刚上线的互联网项目来说,由于前期活跃用户数量并不多,并发量也相对较小,所以此时企业一般都会选择将所有数据存放在 一个数据库 中进行访问操作。但随着后续的市场推广力度不断加强,用户数量和并发量不断上升,这时如果仅靠一个数据库来支撑所有访问压力,几乎是在 自寻死路 。所以一旦到了这个阶段,大部分 Mysql DBA 就会将数据库设置成 读写分离状态 ,也就是一个 Master节点对应多个 Salve 节点。经过 Master/Salve 模式的设计后,完全可以应付单一数据库无法承受的负载压力,并将访问操作分摊至多个 Salve 节点上,实现真正意义上的读写分离。但大家有没有想过,单一的 Master/Salve 模式又能抗得了多久呢?如果用户数量和并发量出现 量级 上升,单一的 Master/Salve 模式照样抗不了多久,毕竟一个 Master 节点的负载还是相对比较高的。为了解决这个难题,Mysql DBA 会在单一的 Master/Salve 模式的基础之上进行数据库的 垂直分区 (分库)。所谓垂直分区指的是可以根据业务自身的不同,将原本冗余在一个数据库内的业务表拆散,将数据分别存储在不同的数据库中,同时仍然保持 Master/Salve模式。经过垂直分区后的 Master/Salve 模式完全可以承受住难以想象的高并发访问操作,但是否可以永远 高枕无忧 了?答案是否定的,一旦业务表中的数据量大了,从维护和性能角度来看,无论是任何的 CRUD 操作,对于数据库而言都是一件极其耗费资源的事情。即便设置了索引, 仍然无法掩盖因为数据量过大从而导致的数据库性能下降的事实 ,因此这个时候 Mysql DBA 或许就该对数据库进行 水平分区 (分表, sharding ),所谓水平分区指的是将一个业务表拆分成多个子表,比如 user_table0 、 user_table1 、 user_table2 。子表之间通过某种契约关联在一起,每一张子表均按段位进行数据存储,比如 user_table0 存储 1-10000 的数据,而 user_table1 存储 10001-20000 的数据,最后 user_table3 存储 20001-30000 的数据。经过水平分区设置后的业务表,必然能够将原本一张表维护的海量数据分配给 N 个子表进行存储和维护,这样的设计在国内一流的互联网企业比较常见,如图 1-1 所示:
图 1-1 水平分区
上述笔者简单的讲解了数据库的分库分表原理。接下来请大家认真思考下。原本一个数据库能够完成的访问操作,现在如果按照分库分表模式设计后,将会显得非常麻烦,这种麻烦尤其体现在 访问操作 上。因为持久层需要判断出对应的数据源,以及数据源上的水平分区,这种访问方式我们称之为访问 “ 路由 ” 。按照常理来说,持久层不应该负责数据访问层 (DAL) 的工作,它应该只关心 one to one 的操作形式,所以淘宝的 TDDL 框架诞生也就顺其自然了。
二、 TDDL 的架构原型
淘宝根据自身业务需求研发了 TDDL ( Taobao Distributed Data Layer )框架,主要用于解决 分库分表场景下的访问路由(持久层与数据访问层的配合)以及异构数据库之间的数据同步 ,它是一个基于集中式配置的 JDBC DataSource 实现,具有分库分表、 Master/Salve 、动态数据源配置等功能。
就目前而言,许多大厂也在出一些更加优秀和社区支持更广泛的 DAL 层产品,比如 Hibernate Shards 、 Ibatis-Sharding 等。如果你要问笔者还为什么还要对 TDDL进行讲解,那么笔者只能很 无奈 的表示公司要这么干,因为很多时候技术选型并不是笔者说了算,而是客户说了算。当笔者费劲所有努力在 google 上寻找 TDDL的相关使用说明和介绍时,心里一股莫名的火已经开始在蔓延,对于更新缓慢(差不多一年没更新过 SVN ),几乎没社区支持(提问从不响应)的产品来说,除了蜗居在企业内部,必定走不了多远,最后的结局注定是 悲哀 的。好了,既然抱怨了一番,无论如何还是要坚持讲解完。 TDDL 位于数据库和持久层之间,它直接与数据库建立交道,如图 1-2 所示:
图 1-2 TDDL 所处领域模型定位
传说淘宝很早以前就已经对数据进行过分库分表处理,应用层连接多个数据源,中间有一个叫做 DBRoute 的技术对数据库进行 统一 的路由访问。 DBRoute 对数据进行多库的操作、数据的整合,让应用层像操作一个数据源一样操作多个数据库。但是随着数据量的增长,对于库表的分法有了更高的要求,例如,你的商品数据到了百亿级别的时候,任何一个库都无法存放了,于是分成 2 个、 4 个、 8 个、 16个、 32 个 …… 直到 1024 个、 2048 个。好,分成这么多,数据能够存放了,那怎么查询它?这时候,数据查询的中间件就要能够承担这个重任了,它对上层来说,必须像查询一个数据库一样来查询数据,还要像查询一个数据库一样快( 每条查询要求在几毫秒内完成 ), TDDL 就承担了这样一个工作( 其他 DAL 产品做得更好 ),如图 1-3 所示:
图 1-3 TDDL 分库分表查询策略
上述笔者描述了 TDDL 在分库分表环境下的查询策略,那么接下来笔者有必要从淘宝官方 copy 它们自己对 TDDL 优点的一些描述,真实性不敢保证,毕竟没完全开源,和社区零支持,大家看一看就算了,别认真。
淘宝人自定的 TDDL 优点:
1 、数据库主备和动态切换;
2 、带权重的读写分离;
3 、单线程读重试;
4 、集中式数据源信息管理和动态变更;
5 、剥离的稳定 jboss 数据源;
6 、支持 mysql 和 oracle 数据库;
7 、基于 jdbc 规范,很容易扩展支持实现 jdbc 规范的数据源;
8 、无 server,client-jar 形式存在,应用直连数据库;
9 、读写次数 , 并发度流程控制,动态变更;
10 、可分析的日志打印 , 日志流控,动态变更;
注意 :
TDDL 必须要依赖 diamond 配置中心( diamond 是淘宝内部使用的一个管理持久配置的系统,目前淘宝内部绝大多数系统的配置)。
接下来,笔者将会带领各位一起分析 TDDL 的体系架构。 TDDL 其实主要可以划分为 3 层架构,分别是 Matrix 层、 Group 层和 Atom 层。 Matrix 层用于实现分库分表逻辑,底层持有多个 Group 实例。而 Group 层和 Atom 共同组成了 动态数据源 , Group 层实现了数据库的 Master/Salve 模式的写分离逻辑,底层持有多个Atom 实例。最后 Atom 层 (TAtomDataSource) 实现数据库ip,port,password,connectionProperties 等信息的动态推送 , 以及持有原子的数据源分离的 JBOSS 数据源)。
图 1-4 TDDL 体系结构
章节的最后,我们还需要对 TDDL 的原理进行一次剖析。因为我们知道持久层只关心对数据源的 CRUD 操作,而多数据源的访问,并不应该由它来关心。也就是说 TDDL 透明给持久层的数据源接口应该是统一且 “ 单一 ” 的,至于数据库 到底如何分库分表,持久层无需知道,也无需 编写对应的 SQL 去实行 应对策略 。这个时候对 TDDL 一些疑问就出现了, TDDL 需要对 SQL 进行二次解析和拼装吗?答案是 不解析仅拼装 。说白了 TDDL 只需要从持久层拿到发出的 SQL
再按照一些分库分表条件,进行特定的 SQL 扩充以此满足访问路路由操作。
以下是淘宝团队对 TDDL 的官方原理解释:
1 、 TDDL 除了拿到分库分表条件外,还需要拿到 order by 、 group by 、 limit 、join 等信息, SUM 、
MAX 、 MIN 等聚合函数信息, DISTINCT 信息。具有这些关键字的 SQL 将会在单库和多库情况下进行 , 语义是不同的。 TDDL 必须对使用这些关键字的 SQL 返回的结果做出合适的处理;
2 、 TDDL 行复制需要重新拼写 SQL, 带上 sync_version 字段;
3 、不通过 sql 解析 , 因为 TDDL 遵守 JDBC 规范 , 它不可能去扩充 JDBC 规范里面的接口 , 所以只能通过 SQL 中加额外的字符条件 ( 也就是 HINT 方式 ) 或者ThreadLocal 方式进行传递 , 前者使 SQL 过长 , 后者难以维护 , 开发 debug 时不容易跟踪 , 而且需要判定是在一条 SQL 执行后失效还是 1 个连接关闭后才失效;
4 、 TDDL 现在也同时支持 Hint 方式和 ThreadLocal 方式传递这些信息;
三、下载 TDDL 的 Atom 层和 Group 层源代码
前面我们谈及了 TDDL 的动态数据源主要由 2 部分构成,分别是 Atom 和Group 。 Group 用于实现数据库的 Master/Salve 模式的写分离逻辑,而 Atom 层则是持有数据源。非常遗憾的 TDDL 中还有一层叫做 Matrix ,该层是整个 TDDL 最为核心的地方,淘宝也并没有对这一层实现开源,而 Matrix 层主要是建立在动态数据源之上的分库分表实现。换句话说, TDDL 是基于模块化结构的,开发人员可以选用 TDDL 中的部分子集。
大家可以从淘宝的 TaoCode 上下载 TDDL 的源码带,然后进行构件的打包。TDDL 的项目主要是基于 Maven 进行管理的,所以建议大家如果不了解 Maven 的使用,还是参考下笔者的博文《 Use Maven3.x 》。
大家下载好 TDDL 的源代码后,通过 IDE 工具导入进来后可以发现,开源的TDDL 的工程结构有如下几部份组成:
tddl-all –
— tbdatasource
— tddl-atom-datasource
— tddl-common
— tddl-group-datasource
— tddl-interact
— tddl-sample
大家可以使用 Maven 的命令“ mvn package “将 TDDL 的源代码打包成构件。如果你的电脑上并没有安装 Maven 的插件到不是没有办法实现构件打包,你可以使用eclipse 的导出命令,将源代码导出成构件形式也可以。
四、 Diamond 简介
使用任何一种框架都需要配置一些配置源信息,毕竟每一种框架都有自己的规范,使用者务必遵守这些规范来实现自己的业务与基础框架的整合。自然 TDDL 也不例外,也是有配置信息需要显式的进行配置,在 TDDL 中,配置可以基于 2 种方式,一种是基于本地配置文件的形式,另外一种则是基于 Diamond 的形式进行配置,在实际开发过程中,由于考虑到配置信息的集中管理所带来的好处,大部分开发人员愿意选择将 TDDL 的配置信息托管给 Diamond ,所以本文还是以Diamond 作为 TDDL 的配置源。
diamond 是淘宝内部使用的一个管理持久配置的系统,它的特点是简单、可靠、易用,目前淘宝内部绝大多数系统的配置,由 diamond 来进行统一管理。diamond 为应用系统提供了获取配置的服务,应用不仅可以在启动时从 diamond获取相关的配置,而且可以在运行中对配置数据的变化进行感知并获取变化后的配置数据。
五、 Diamond 的安装和使用
Diamond 和 TDDL 不同,它已经实现了完全意义上的开源。大家可以从淘宝的TaoCode
上下载 Diamond 的源代码, SVN 下载地址为http://code.taobao.org/svn/diamond/trunk 。当大家成功下载好 Diamond 的源代码后,我们接下来就需要开始 Diamond 的环境搭建工作。
首先我们需要安装好 Mysql 数据库,以 root 用户登录,建立用户并赋予权限,建立数据库,然后建表,语句分别如下:
create database diamond;
grant all on diamond.* to zh@’%’ identified by ‘abc’;
use diamond
create table config_info (
‘ id’ bigint(64) unsigned NOT NULL auto_increment,
‘ data_id’ varchar(255) NOT NULL default ’ ’,
‘ group_id’ varchar(128) NOT NULL default ’ ’,
‘ content’ longtext NOT NULL,
‘ md5 ′ varchar(32) NOT NULL default ’ ’ ,
‘ gmt_create ’ datetime NOT NULL default ’ 2010-05-05 00:00:00 ′ ,
‘ gmt_modified ’ datetime NOT NULL default ’ 2010-05-05 00:00:00 ′ ,
PRIMARY KEY (‘id’),
UNIQUE KEY ‘uk_config_datagroup’ (‘data_id’,'group_id’));
完成后,请将数据库的配置信息( IP ,用户名,密码)添加到 diamond-server 工程的 src/resources/jdbc.properties 文件中的 db.url , db.user , db.password 属性上面,这里建立的库名,用户名和密码,必须和 jdbc.properties 中对应的属性相同。
tomcat 是 Damond 的运行容器,在 diamond-server 源代码根目录下,执行 mvn clean package -Dmaven.test.skip ,成功后会在 diamond-server/target 目录下生成diamond-server.war 。打包完成后,将 diamond-server.war 放在 tomcat 的webapps 目录下。最后启动 tomcat ,即启动了 Diamond 。
http server 用来存放 diamond server 等地址列表,可以选用任何 http server ,这里以 tomcat 为例。一般来讲, http server 和 diamond server 是部署在不同机器上的,这里简单起见,将二者部署在同一个机器下的同一个 tomcat 的同一个应用中,注意,如果部署在不同的 tomcat 中,端口号一定是 8080 ,不能修改(所以必须部署在不同的机器上)。
在 tomcat 的 webapps 中的 diamond-server 中建立文件 diamond ,文件内容是diamond-server 的地址列表,一行一个地址,地址为 IP ,例如 127.0.0.1 ,完成这些步骤后,就等于已经完成 Diamond 的安装。
六、动态数据源层的 Master/Salve 读写分离 配置与实现
其实使用 TDDL 并不复杂,只要你会使用 JDBC ,那么 TDDL 对于你来说无非就只需要将 JDBC 的操作连接替换为 TDDL 的操作连接,剩余操作一模一样。并且由于 TDDL 遵循了 JDBC 规范,所以你完全还可以使用 Spring JDBC 、 Hibernate等第三方持久层框架进行 ORM 操作。
我们来看看如何 TDDL 中配置 TDDL 的读写分离, Atom+Group 组成了 TDDL 的动态数据源,这 2 层主要负责数据库的读写分离。
TGroupDataSource 的配置
1、 配置读写分离权重:
KEY : com.taobao.tddl.jdbc.group_V2.4.1_ “ groupKey ” (Matrix 中为“ dbKey ” )
VALUE : dbKey:r10w0,dbKey2:r0w10
TAtomDataSource 的配置(由 3 部分组成, global 、 app 、 user )
1、 基本数据源信息 (global) :
KEY : com.taobao.tddl.atom.global. “ dbKey ”
VALUE :(
ip= 数据库 IP
port= 数据库端口
dbName= 数据库昵称
dbType= 数据库类型
dbStatus=RW )
2、 数据库密码信息 (user) :
KEY : com.taobao.tddl.atom.passwd. “ dbKey ” . “ dbType ” . “ dbUserName ”
VALUE :数据库密码
3、 数据库连接信息( app ,如果不配置时间单位,缺省为分钟):
KEY : com.taobao.tddl.atom.app. “ appName ” . “ dbKey ”
VALUE :(
userName= 数据库用户
minPoolSize= 最小连接数
maxPoolSize= 最大连接数
idleTimeout= 连接的最大空闲时间
blockingTimeout= 等待连接的最大时间
checkValidConnectionSQL=select 1
connectionProperties=rewriteBatchedStatements=true&characterEncoding=UTF8&connectTimeout=1000&autoReconnect=true&socketTimeout=12000 )
应用层使用 TDDL 示例:
public class UseTDDL private static final String APPNAME = "tddl_test"; private static final String GROUP_KEY = "tddltest"; private static TGroupDataSource tGroupDataSource ; /* 初始化动态数据源 */ static tGroupDataSource = new TGroupDataSource(); tGroupDataSource .setAppName( APPNAME ); tGroupDataSource .setDbGroupKey( GROUP_KEY ); tGroupDataSource .init();
@Test public void testQuery() final String LOAD_USER = "SELECT userName FROM tddl_table WHERE userName=?"; Connection conn = null ; PreparedStatement pstmt = null ; ResultSet rs = null ; try conn = tGroupDataSource .getConnection(); pstmt = conn.prepareStatement(LOAD_USER); pstmt.setString(1, "tddl-test2"); rs = pstmt.executeQuery(); while (rs.next()) System. out .println("data: " + rs.getString(1)); catch (Exception e) e.printStackTrace(); finally try if ( null != rs) rs.close(); if ( null != pstmt) pstmt.close(); if ( null != conn) conn.close(); catch (Exception e) e.printStackTrace();
MySQL UNION 的两个子查询是串行还是并行 mysql 拆分数据库 现在我的Mysql下面的一个库内有很多表,我想把老表迁移走放在另外一个库下面,如何完成 |