Zdal分库分表介绍、超详细一步一步搭建简单的zdal框架
来源:互联网 发布:淘宝关键词在哪里找 编辑:程序博客网 时间:2024/05/29 11:01
Zdal分库分表、超详细一步一步实现使用zdal搭建框架
转载请标明出处,谢谢~!^^,有问题一起讨论 _______by-陶浩伟
背景:
我在的开发小组比较空闲,在看完业务逻辑后,暂时没什么任务的我研究起了小组项目的搭建。
小组项目是一个分布式项目,逻辑比较简单,但是用到的技术比较全面。分别用到 dubbo分布式服务框架、MQ消息队列、redis、Zdal分库分表、Mybatis、spring等
技术在业务逻辑上的体现大致为:
正文
1、什么是zdal? 它能解决什么问题?
2、zdal的组成
3、搭建的项目展示
4、zdal搭建所需环境
5、zdal配置文件的配置
6、编写测试类,测试项目搭建
7、异常处理建议
1、什么是zdal? 它能解决什么问题?
注:本文参照了些该文(同后)的内容,加上自己的理解,侧重实际搭建,内容有表述不清楚的地方,请参照文档理解–作者:黄磊,若有错误知错,请大家指出^^。
1.1 什么是zdal?
Zdal是支付宝自主研发的数据中间件产品,采用标准的JDBC规范,可以在分布式环境下看上去像传统数据库一样提供海量数据服务,是一种通用的分库分表数据库访问框架。
1.2 它能解决什么问题?
它能在数据访问压力过大时,解决单库单表数据库访问压力,Zdal主要提供分库分表,分散压力,结果集合并,sql解析,数据库failover动态切换等功能,提供互联网金融行业的数据访问层统一解决方案,
1.3 实际运用
目前已经在支付宝的交易,支付,会员,金融等大部分关键应用上使用,并且在2013年双11大促中运行稳定。
2、zdal的组成、zdal是怎么实现的?
2.1 zdal组件主要有5部分组成:
- Zdal-client:开发编程接口,实现jdbc的Datasource,Connection,Statement,PreparedStatement,ResultSet等接口,实现通用的jdbc-sql访问,内部还实现读库重试,group数据源的选择器,表名替换,sql执行器等功能。
- Zdal-parser:支持oracle/mysql/db2等数据库的sql语句解析,并且缓存。根据规则引擎提供的参数列表,在指定的sql中查找到需要的参数,然后返回拆分字段。
- Zdal-rule:根据zdal-parser解析后的拆分字段值来确定逻辑库和物理表名。
- Zdal-datasource:数据库连接的管理,支持mysql,oracle,db2数据库的连接管理。
- Zdal-common:zdal组件所使用的一些公共组件类
2.3 zdal是怎么实现的呢?
a、不同于以往的jdbc连接,zdal实现同时与多个数据源绑定
b、根据规则引擎生成的目标库id和表名,根据其中的某个字段计算该数据属于的物理数据库
c、然后动态指定读库,对该库进行操作
3、搭建的项目展示
3.1 项目介绍
项目一共使用两个数据库服务器,分别是本机和远程的数据库服务器,共分了4个数据库DB,平均分布共12张城市信息表,每个数据库中3张城市信息表,城市信息表中只有两个字段,分别是id和cityName.
数据库DB命名规则为test_XX(索引),城市信息表命名规则为t_city_XX(索引)
3.2 数据库目录结构、以及表展示
//表的创建sql语句CREATE TABLE test_00.t_city_00 ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID', `cityName` varchar(32) NOT NULL COMMENT '城市名', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;`
3.3 项目目录结构展示
4、zdal搭建所需环境
1、zdal需要本身自己的jar包,可以点击下载,百度云密码:9e9x。
2、需要spring相关jar包
3、需要jdbc-driver-mysql的jar包
4、项目中使用了mybatis、所以还需要mybatis相关jar包。
5、如有需要可引入log相关jar包
使用时可以下载zdal包后,解压到maven的仓库中,然后如下配置
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>test1</groupId> <artifactId>zdal_test</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project_version>1.0-SNAPSHOT</project_version> <spring_version>3.2.9.RELEASE</spring_version> <junit_version>4.10</junit_version> <mybatis-spring_version>1.2.0</mybatis-spring_version> <zdal.version>1.0</zdal.version> </properties> <dependencies> <!--zdal相关--> <dependency> <groupId>com.alipay.zdal</groupId> <artifactId>zdal-client</artifactId> <version>${zdal.version}</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> </exclusions> </dependency> <!--junit测试jar包 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit_version}</version> <scope>test</scope> </dependency> <!-- Mybaits相关 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring_version}</version> </dependency> <!-- Mysql连接相关 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.36</version> </dependency> <!-- Spring相关 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring_version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring_version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring_version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring_version}</version> </dependency> </dependencies></project>
5、zdal配置文件的配置
5.1 完成zdal-rule类
zdal-rule主要是完成规则的计算,包括分库的计算和分表的计算,相当于是一个二次路由的过程,这里使用在代码里封装拆分规则静态方法,在规则里调用该静态方法。
通俗的说,就是为了zdal判断,这条数据是存在哪个数据库服务器,哪个数据库的哪个数据表
这里我写的计算过程很简单,就是根据城市id来计算的,代码如下:
package com.util;/** * Created by Administrator on 2017/7/20. */public class ZdalRuleParser { /** * 解析得到分库的数据库 * @param id * @return */ public static int parserDbIndex(int id) { //因为一共四个库,直接对4取余即可取到所有的数据库(0,1,2,3) return id%4; } /** * 解析得到分表的表结构 * @param id * @return */ public static int parserTbIndex(int id) { int index = parserDbIndex(id); //{(0-0,1,2),(1-3,4,5),(2-6,7,8)}-索引为0的数据库对应0,1,2三张表、索引为1的数据库对应3,4,5三张表 //这里根据数据库索引,乘3+id对3取余,即可对得到对应的表 //例如,id为3时,数据库索引为3,表索引为9.则这条数据插入在test_03中的t_city_09中 int tbIndex = index*3+id%3; return tbIndex; }}
5.2 完成zdal-rule配置文件
这里的配置规则可以参考1中的文档理解,里面对每条property属性含义有比较全面的解释。
—图片转自文档理解—
—图片转自文档理解—
这个配置文件对应 3.3 项目目录结构展示 中的/resource/config/zdal-dev-rule.xml文件,主要实现内容是关联上述5.1中的配置类,分别对应数据库、表的计算,代码如下
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="testRule" class="com.alipay.zdal.rule.config.beans.AppRule"> <property name="masterRule" ref="testRWRule"/> <!--<property name="readwriteRule" ref="testRWRule"/>--> </bean> <bean id="testRWRule" class="com.alipay.zdal.rule.config.beans.ShardRule"> <property name="tableRules"> <map> <entry key="t_city" value-ref="userTableRule"/> </map> </property> </bean> <bean id="userTableRule" class="com.alipay.zdal.rule.config.beans.TableRule" init-method="init"> <property name="tbSuffix" value="resetForEachDB:[_00-_11]"/> <property name="dbIndexes" value="master_00,master_01,master_02,master_03"/> <!-- 分库解析方法 --> <property name="dbRuleArray"> <list> <value> return com.util.ZdalRuleParser.parserDbIndex(#id#); </value> </list> </property> <!-- 分表解析方法 --> <property name="tbRuleArray"> <list> <value> return com.util.ZdalRuleParser.parserTbIndex(#id#); </value> </list> </property> </bean></beans>
5.3 完成zdal-ds数据源配置文件
这里的配置规则可以参考1中的文档理解,里面对每条property属性含义有比较全面的解释。
—图片转自文档理解—
—图片转自文档理解—这个配置文件对应 3.3 项目目录结构展示 中的/resource/config/zdal-dev-ds.xml文件,主要实现是关联zdal-dev-rule.xml文件。将数据库源连接,这里需要注意的几点有:
a、两个配置文件的名称,需要与ZdalAppBean中的appName、dbmode相关联,格式为 appName-dbmode-ds/rule.xml 如果名称不按格式来,zdal初始化时会加载不成功。
b、配置数据源时(也就是PhysicalDataSourceBean),多个数据库服务器需要配置多个对应数据库连接源,然后在(AppDataSourceBean)中绑定,形成一对多的数据库结构。具体参考如下代码:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- zdal数据源配置 --> <bean id="zdal" class="com.alipay.zdal.client.config.bean.ZdalAppBean"> <property name="appName" value="zdal"/> <property name="dbmode" value="dev"/> <property name="appDataSourceList"> <list> <ref bean="ShardDataSource"/> </list> </property> </bean> <bean id="ShardDataSource" class="com.alipay.zdal.client.config.bean.AppDataSourceBean"> <property name="appDataSourceName" value="ShardDataSource"/> <property name="dataBaseType" value="MYSQL"/> <property name="configType" value="Shard"/> <property name="appRule" ref="testRule"/> <property name="physicalDataSourceSet"> <set> <ref bean="testDB_00"/> <ref bean="testDB_01"/> <ref bean="testDB_02"/> <ref bean="testDB_03"/> </set> </property> </bean> <bean id="testDB_00" class="com.alipay.zdal.client.config.bean.PhysicalDataSourceBean"> <property name="name" value="master_00"/> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test_00?autoReconnect=true&useUnicode=true&characterEncoding=utf8"/> <property name="userName" value="root"/> <property name="password" value="0203"/> <property name="minConn" value="1"/> <property name="maxConn" value="10"/> <property name="blockingTimeoutMillis" value="180"/> <property name="idleTimeoutMinutes" value="180"/> <property name="preparedStatementCacheSize" value="100"/> <property name="queryTimeout" value="180"/> <property name="prefill" value="true"/> <property name="connectionProperties"> <map> <entry key="connectTimeout" value="500"/> <entry key="autoReconnect" value="true"/> <entry key="initialTimeout" value="1"/> <entry key="maxReconnects" value="2"/> <entry key="socketTimeout" value="5000"/> <entry key="failOverReadOnly" value="false"/> </map> </property> </bean> <bean id="testDB_01" class="com.alipay.zdal.client.config.bean.PhysicalDataSourceBean"> <property name="name" value="master_01"/> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test_01?autoReconnect=true&useUnicode=true&characterEncoding=utf8"/> <property name="userName" value="root"/> <property name="password" value="0203"/> <property name="minConn" value="1"/> <property name="maxConn" value="10"/> <property name="blockingTimeoutMillis" value="180"/> <property name="idleTimeoutMinutes" value="180"/> <property name="preparedStatementCacheSize" value="100"/> <property name="queryTimeout" value="180"/> <property name="prefill" value="true"/> <property name="connectionProperties"> <map> <entry key="connectTimeout" value="500"/> <entry key="autoReconnect" value="true"/> <entry key="initialTimeout" value="1"/> <entry key="maxReconnects" value="2"/> <entry key="socketTimeout" value="5000"/> <entry key="failOverReadOnly" value="false"/> </map> </property> </bean> <bean id="testDB_02" class="com.alipay.zdal.client.config.bean.PhysicalDataSourceBean"> <property name="name" value="master_02"/> <property name="jdbcUrl" value="jdbc:mysql://47.94.107.115:3306/test_02?autoReconnect=true&useUnicode=true&characterEncoding=utf8"/> <property name="userName" value="root"/> <property name="password" value="这里填服务器密码"/> <property name="minConn" value="1"/> <property name="maxConn" value="10"/> <property name="blockingTimeoutMillis" value="180"/> <property name="idleTimeoutMinutes" value="180"/> <property name="preparedStatementCacheSize" value="100"/> <property name="queryTimeout" value="180"/> <property name="prefill" value="true"/> <property name="connectionProperties"> <map> <entry key="connectTimeout" value="500"/> <entry key="autoReconnect" value="true"/> <entry key="initialTimeout" value="1"/> <entry key="maxReconnects" value="2"/> <entry key="socketTimeout" value="5000"/> <entry key="failOverReadOnly" value="false"/> </map> </property> </bean> <bean id="testDB_03" class="com.alipay.zdal.client.config.bean.PhysicalDataSourceBean"> <property name="name" value="master_03"/> <property name="jdbcUrl" value="jdbc:mysql://47.94.107.115:3306/test_03?autoReconnect=true&useUnicode=true&characterEncoding=utf8"/> <property name="userName" value="root"/> <property name="password" value="这里填服务器密码"/> <property name="minConn" value="1"/> <property name="maxConn" value="10"/> <property name="blockingTimeoutMillis" value="180"/> <property name="idleTimeoutMinutes" value="180"/> <property name="preparedStatementCacheSize" value="100"/> <property name="queryTimeout" value="180"/> <property name="prefill" value="true"/> <property name="connectionProperties"> <map> <entry key="connectTimeout" value="500"/> <entry key="autoReconnect" value="true"/> <entry key="initialTimeout" value="1"/> <entry key="maxReconnects" value="2"/> <entry key="socketTimeout" value="5000"/> <entry key="failOverReadOnly" value="false"/> </map> </property> </bean></beans>
5.4 完成城市相关逻辑,以及mapper配置文件
接下来就很简单,城市相关逻辑很简单,逻辑只实现包含一条向数据库中插入一条城市信息。{(id,1),(cityName,北京)},直接看代码就可以好。如下:
//biz包--即CityService,具体所处位置可参考3.3项目目录结构图@Servicepublic class CityService { @Autowired private CityMapper cityMapper; public void insert(City city) { System.out.println("开始插入"); int i = cityMapper.insertCity(city); System.out.println("插入结果为:"+i); }}
//mapper包--即CityMapper接口,接口位置请参考3.3项目结构目录public interface CityMapper { public int insertCity(City city);}//对应/resources/mapper/cityMapper.xml配置文件<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.mapper.CityMapper"> <insert id="insertCity" parameterType="com.model.City"> INSERT INTO t_city (id,cityName) VALUES (#{id},#{cityName}) </insert></mapper>
//model包--City模型public class City { private int id; private String cityName; //........省略构造方法........ //........省略getter和setter........ }
5.5 完成spring配置文件
spring配置文件也很简单,只需要配置zdal分库分表的数据源,将mybatis数据源与其绑定即可。
注意:这里的dbmode、appname和appDsName与zdal-dev-ds中的相对应
代码实现如下:
<?xml version="1.0" encoding="GBK"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd"> <!-- --> <context:annotation-config /> <context:component-scan base-package="com.biz"/> <!-- 分库分表数据源--> <bean id="dataSource" class="com.alipay.zdal.client.jdbc.ZdalDataSource" init-method="init"> <property name="appName" value="zdal"/> <property name="appDsName" value="ShardDataSource"/> <property name="dbmode" value="dev"/> <property name="configPath" value="./config"/> <!--<property name="configPath" value="classpath*:mapper/*.xml"/>--> </bean> <!-- mybatis相关配置,绑定数据源 --> <mybatis:scan base-package="com.mapper"/> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.model"/> <property name="mapperLocations" value="classpath*:mapper/*.xml"/> </bean></beans>
6、编写测试类,测试项目搭建
至此,配置相关的内容结束、实际操作也很方便,重点在zdal对我们熟悉的dao层编程几乎没有任何入侵,只需要在对数据库操作前,查询库的路由规则(咱们在ZdalRuleParser类中自己写的那个实现类),得到实际操作的数据。
内容步骤如下:
1、初始化spring容器,得到数据库对应的数据源
2、从数据源中得到 逻辑物理数据源名称(logicPhysicsDsNames),用于初始化logicPhysicsIndexes数据源集合
3、开始执行插入操作,通过在ZdalRuleParser类写的映射规则,使用递增id计算得到这条数据分库分表的索引。(例如id=3,分库对应test_03,分表对应t_city_09)
4、将计算得到的分表组成数据库表示路由条件,然后将这个对象添加到线程中。(这两步想当于告诉接下来数据库插入的操作,你要往分库对应test_03,分表对应t_city_09这个数据源中插入数据,别插错了。)
5、执行插入操作
import com.alipay.zdal.client.ThreadLocalString;import com.alipay.zdal.client.jdbc.ZdalDataSource;import com.alipay.zdal.client.util.ThreadLocalMap;import com.alipay.zdal.client.util.condition.DBSelectorIDRouteCondition;import com.biz.CityService;import com.model.City;import com.util.ZdalRuleParser;import org.junit.Before;import org.junit.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.text.DecimalFormat;import java.util.HashMap;import java.util.Map;/** * Created by Administrator on 2017/7/20. */public class MyTest { private CityService service; private ZdalDataSource dataSource; // 所有配置的物理数据源, dbIndex private Map<Integer, String> logicPhysicsIndexes; //库名 private String dbName = "test"; //表名 private String tbName = "t_city"; @Before public void init() { //加载spring容器,实例化bean ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); service = (CityService) context.getBean("cityService"); dataSource = (ZdalDataSource) context.getBean("dataSource"); //实例化逻辑数据源 logicPhysicsIndexes = new HashMap<Integer, String>(); //得到物理数据源名称 Map<String, String> logicPhysicsDsNames = dataSource.getZdalConfig().getLogicPhysicsDsNames(); //将其存入物理数据源数据 {(0,master_00),(1,master_01)...} for (String name : logicPhysicsDsNames.keySet()) { String[] split = name.split("_"); logicPhysicsIndexes.put(Integer.valueOf(split[1]), name); } } @Test /** * 插入数据库操作 */ public void insert(){ //设置插入10条id数据,id自增 for (int id=30;id<40;id++) { // 指定查詢库的路由規則 //根据规则得到分库索引 int dbIndex = ZdalRuleParser.parserDbIndex(id); String dbSelectorID = logicPhysicsIndexes.get(dbIndex); System.out.println("dbSelectorID = " + dbSelectorID); //根据规则得到分表索引 int tbIndex = ZdalRuleParser.parserTbIndex(id); String tablePostfix = new DecimalFormat("_00").format(tbIndex);//如果tbIndex为3,tablePostfix为_03 //根据分表索引得到物理表名称 String physicTableName = tbName + tablePostfix; System.out.println("physicTableName = " + physicTableName);//physicTableName = t_city_00、t_city_01、t_city_02等 //得到 数据库选择器标识路由条件---确定是存在哪一个数据库中 DBSelectorIDRouteCondition dbSelectorIDRouteCondition = new DBSelectorIDRouteCondition( "t_city", dbSelectorID, physicTableName); //将分库分表添加到线程中 ThreadLocalMap.put(ThreadLocalString.ROUTE_CONDITION, dbSelectorIDRouteCondition); System.out.println("配置完成,准备开始插入数据"); City city = new City(id, "北京"); service.insert(city); } }}
运行结果:
Run输出台:
数据库数据:
7、异常处理建议
配置过程中,不要错漏掉某些数据,在运行测试类时,如果碰上异常,耐心的看它报的错误指向,观察zdal的源码,观察它在出错的那一段是想要做什么事情,抛出的异常,然后根据这个逻辑,去寻找到底是哪一块出错,耐心一些^^
结束
转载请标明出处,谢谢~!^^,有问题一起讨论 _______by-陶浩伟
- Zdal分库分表介绍、超详细一步一步搭建简单的zdal框架
- Zdal分库分表
- 构建分库分表中间件Zdal
- 在项目中使用分库分表中间件Zdal
- springmvc4+hibernate4整合框架的搭建,超详细哦
- springmvc4+hibernate4整合框架的搭建,超详细哦
- 超简单的iOS主流MVC APP框架搭建
- 2.2 maven基础上搭建springmvc4.X,一步一步超简单。
- Hibernate 框架的搭建及简单介绍
- ASIHTTPRequest的超详细介绍
- 基于maven的ssh框架一步一步搭建
- 基于maven的ssh框架一步一步搭建
- 基于maven的ssh框架一步一步搭建
- 基于maven的ssh框架一步一步搭建
- Hibernate 框架的详细搭建与简单使用(一)
- SSH框架一步一步搭建
- 【超详细图文教程】用SpringBoot+Maven搭建SSM框架
- Python 并行分布式框架:Celery 超详细介绍
- obiee 导出excel,pdf报错
- 牛顿迭代法 简单入门
- java数组操作简介
- C++开源库大全
- collapse模态框隐藏
- Zdal分库分表介绍、超详细一步一步搭建简单的zdal框架
- hdfs的数据压缩
- Android bug管理
- 编写一个简单的程序,用于校对选择题答案
- 如何使用echart的Graph图实现一个流程控制图
- 开发遇坑
- Unity滑动折线图开发
- HDFS与其他并行文件系统的比较
- 《Java设计模式之策略模式》