Zdal是支付宝自主研发的数据中间件产品,采用标准的JDBC规范,可以在分布式环境下看上去像传统数据库一样提供海量数据服务,是一种通用的分库分表数据库访问框架,解决单库单表数据库访问压力,Zdal主要提供分库分表,结果集合并,sql解析,数据库failover动态切换等功能,提供互联网金融行业的数据访问层统一解决方案,目前已经在支付宝的交易,支付,会员,金融等大部分关键应用上使用,并且在2019双11大促中运行稳定。
系统目标
1.数据访问路由,将针对数据的读写请求发送到最合适的地方。
2.数据存储的自由扩展,不再受限于单台机器的容量瓶颈和速度瓶颈,平滑迁移。
3.使用zdal组件进行数据库的拆分,搭建分布式环境下的海量数据访问平台。
4.实现mysql,oracle,DB2数据库访问能力。
系统架构和领域模型
系统整体架构
![348e395f2b6569a3da9ffe84d0cd2fbd.png](https://i-blog.csdnimg.cn/blog_migrate/2d8f47c6dc52790a0ff362c2c9b5f399.png)
zdal组件主要有5部分组成:
1. Zdal-client:开发编程接口,实现jdbc的Datasource,Connection,Statement,PreparedStatement,ResultSet等接口,实现通用的jdbc-sql访问,内部还实现读库重试,group数据源的选择器,表名替换,sql执行器等功能。
2. Zdal-parser:支持oracle/mysql/db2等数据库的sql语句解析,并且缓存。根据规则引擎提供的参数列表,在指定的sql中查找到需要的参数,然后返回拆分字段。
3. Zdal-rule:根据zdal-parser解析后的拆分字段值来确定逻辑库和物理表名。
4. Zdal-datasource:数据库连接的管理,支持mysql,oracle,db2数据库的连接管理。
5. Zdal-common:zdal组件所使用的一些公共组件类。
总体流程
![d64d336098d17b4d24a698b9b2b83b92.png](https://i-blog.csdnimg.cn/blog_migrate/9bcd23842e48c83317a7832e8a11722a.png)
Zdal初始化流程
![50707fa168f41164d0268701e4744ede.png](https://i-blog.csdnimg.cn/blog_migrate/a4217c7cbdd3711d8944b7b340370a04.png)
分库分表初始化流程
![76aeb824c1b46478d7542c728205dc5a.png](https://i-blog.csdnimg.cn/blog_migrate/5001b287ec476d8983bbb1ba1c9fc9dc.png)
分库分表sql执行流程
![763e480d95139de1abb406d6e1183b94.png](https://i-blog.csdnimg.cn/blog_migrate/125d679382bf110176409ffcb5801f9d.png)
关键技术&第三方框架
Zdal-client
Zdal-client 模块主要是完成以下几部分工作:
1.加载配置文件进行初始化工作,初始化groovy规则引擎。
2.对jdbc 标准接口的封装,包括 DataSource、Connection、Statement、PrepareStatment等,并提供一个一对多的管理容器,可以管理多个jdbc建立的资源。
3.SQL执行:根据规则引擎生成的目标库id和表名,进行表名替换后在目标库上执行该sql,如果是跨库跨表的sql,需要进行多个结果集的merge。
4.读库重试,即在读库发生断连接问题的时候,Zdal会自动的尝试从对等的其他读库中去查询这条数据,尽最大努力保证在数据库还有访问能力的情况下,保证数据的可访问性。
5.将传入的sql 和 参数进行包装后,调用 zdal-parser 和zdal-rule的 相关接口,进行sql的解析以及计算相应的分库分表结果。在此模块将会实现表名替换的功能,即将sql的逻辑表名替换成带后缀的物理表名。
6.动态指定读库功能,即可以让业务根据实际需求指定一组中的某个读库进行操作,也可以指定到写库读。
7.聚合函数结果集合并,针对count,sum,max,min等聚合函数在多个数据源的执行结果,进行结果集的合并。
Zdal-parser
Parser组件包括如下几个部分:
1. Lexer 词法解析。
2.Parser,Parser包括ExprParser,各种StatementParser。
3. AST, Abstract Syntax TreeParse出来的结果就是AST。
4.StatementParser:解析各种sql语句,按照词法分析和语法分析提炼sql的关键字。
5.Visitor:根据StatementParser的解析结果对AST做各种处理,比如FormatOutput,遍历,tableName,表达式,函数,绑定参数,分页参数,获取sql解析的结果。
Zdal-Parser的主要核心类图如下:
![e6735dc9db8ec880dd290d079bfa0396.png](https://i-blog.csdnimg.cn/blog_migrate/d1edfedfb19a332905a49c062d28883f.png)
Zdal-rule
Zdal-rule 主要是完成规则的计算,包括分库的计算和分表的计算,相当于是一个二次路由的过程,包括单库单表、单库多表以及多库多表等几种情况。为了适应规则的灵活配置,目前主要是采用书写groovy脚本的方式来配置规则,或者在代码里封装拆分规则静态方法,在规则里调用该静态方法即可。
我们假设分库规则是 user_id % 9 / 3 分表规则是user_id % 9 % 3
![a0324670c4b2fb1ab0ade3a033512ef3.png](https://i-blog.csdnimg.cn/blog_migrate/f819b96b1f308f10aaf9b94f9e7f1b56.png)
由上图可以看到,其实规则本身可以抽象为两次一对多的过程:▪ 第一次是从逻辑表,通过分库规则, 选择了库
▪ 第二次是从database,通过分表规则,选择了真正的表。
▲Zdal-commonZdal功能所用到一些公共组件类,包括StringUtils,BeanUtils,DataUtils等公共工具类。
>>外围jar包依赖:
1. spring:通过spring-bean来加载zdal的配置文件。
![018347ac1e277299b1cf8724cea43cac.png](https://i-blog.csdnimg.cn/blog_migrate/75595274880df8a444e0e5393f90fe2f.png)
2. log4j:zdal记录的log信息。
![5108090afa20354a816e130d0a724b68.png](https://i-blog.csdnimg.cn/blog_migrate/5e45783bac182099a8b7d9afffc1470b.png)
3. jdbc-driver-oracle:zdal访问oracle数据库的driver。
![df171cb672d5fb7bc2f39f5aea1b0021.png](https://i-blog.csdnimg.cn/blog_migrate/3c6f401ee17c534a2754233e79c47fac.png)
4. jdbc-driver-mysql:zdal访问mysql数据库的driver。
![102638154e44b8051c5b9d01607782c2.png](https://i-blog.csdnimg.cn/blog_migrate/325c368e96bc3c2c3b1b208f7b96b5d9.png)
6. groovy:zdal基于groovy语言实现的一种rule-engine。
![51df7bb27279c438e13870d8d00d54d4.png](https://i-blog.csdnimg.cn/blog_migrate/045f8bd7bb3be9aa6bd3812d7c131076.png)
数据源配置
Zdal的配置采用4层结构,如下图所示:
![8bf930b754164e24372becd204802a84.png](https://i-blog.csdnimg.cn/blog_migrate/dc483c0f13c7d9c94492c7ea2a63f18a.jpeg)
分库分表规则AppRule的3层配置结构如下图:
![653c17bd76cc48f3b2b228bef808b800.png](https://i-blog.csdnimg.cn/blog_migrate/ed47b2372cd358fee2d4f2645d366a99.png)
Group
![a69d28783ecead2b27a32ee693fa50f9.png](https://i-blog.csdnimg.cn/blog_migrate/debd90d3c1d668877ded4f3aa30ad39e.png)
代码示例
示例代码中有group,shard,shardfailover,shardgroup 4中配置的功能测试代码,是基于mysql数据库的,相应的配置文件,分库分表静态类,建库脚本等也在里面:
![86ace0d0c66f1b4a3b769bbf3991c604.png](https://i-blog.csdnimg.cn/blog_migrate/08c11e4c13e33ff2658b5d34ece22218.jpeg)
系统日志
需要在log4j.xml中加上以下的日志配置项:
![fb949e956ace016ab4ad8ff0a04da358.png](https://i-blog.csdnimg.cn/blog_migrate/540ff4da4a7da45b94a3959c7d9b4b1c.jpeg)
![caeff2d0d644ad882e76969ce0f831ea.png](https://i-blog.csdnimg.cn/blog_migrate/13131e77ca4f718c43465dd95a2b5517.jpeg)
Zdal提供两类日志:
1.zdal-client-config.log:初始化的日志信息,主要打印appName,appDsName,dbmode,configPath,分库分表规则,groupRule,failoverRule,动态failover,markdown,markup等动态推送的日志信息。
2.zdal-datasource-pool.log:各个物理数据源连接池的连接状态日志信息;超时连接剔除的日志,异常连接的日志信息等。
数据库灾备
数据访问层组件是以jar包的方式嵌入app中,相应的DB灾备由app和DBA决定,在数据库出现故障需要failover,markdown,markup时,通过分布式环境的zookeeper等工具进行动态切换,zdal目前支持各个数据源权重的动态切换(这部分功能需要由使用方进行改造以便支持)。