mysql数据库分库分表shardingjdbc
分库分表理解
分库分表应用于互联网的两个场景;大量数据和高并发,通常策略有两种:垂直分库,水平拆分
垂直拆分:是根据业务将一个库拆分为多个库,将一个表拆分为多个表,例如:将不常用的字段和经常访问的字段分开存放,在实际开发由于跟业务关系紧密,所以一般采用水平拆分。
水平拆分:则是根据分片算法讲一个库拆分为多个库,来进行维护,与垂直拆分不同,水平拆分是按照一定的规则进行拆分,将不同的数据拆分至不同的物理库。
关系型数据库在大于一定数据量的情况下检索性能会急剧下降。在面对互联网海量数据情况时,所有数据都存于一张表,显然会轻易超过数据库表可承受的数据量阀值。这个单表可承受的数据量阀值,需根据数据库和并发量的差异,通过实际测试获得。
单纯的分表虽然可以解决数据量过大导致检索变慢的问题,但无法解决过多并发请求访问同一个库,导致数据库响应变慢的问题。所以通常水平拆分都至少要采用分库的方式,用于一并解决大数据量和高并发的问题。这也是部分开源的分片数据库中间件只支持分库的原因
但分表也有不可替代的适用场景。最常见的分表需求是事务问题。同在一个库则不需考虑分布式事务,善于使用同库不同表可有效避免分布式事务带来的麻烦。目前强一致性的分布式事务由于性能问题,导致使用起来并不一定比不分库分表快。目前采用最终一致性的柔性事务居多。分表的另一个存在的理由是,过多的数据库实例不利于运维管理。综上所述,最佳实践是合理地配合使用分库+分表。
sharding-jdbc
Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据·库分库分表访问。Sharding-JDBC是继dubbox和elastic-job之后,ddframe系列开源的第3个项目。
Sharding-JDBC直接封装JDBC API,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零:
- 可适用于任何基于Java的ORM框架,如JPA、Hibernate、Mybatis、Spring JDBC Template或直接使用JDBC。
- 可基于任何第三方的数据库连接池,如DBCP、C3P0、 BoneCP、Druid等。
- 理论上可支持任意实现JDBC规范的数据库。虽然目前仅支持MySQL,但已有支持Oracle、SQLServer等数据库的计划。
Sharding-JDBC定位为轻量Java框架,使用客户端直连数据库,以jar包形式提供服务,无proxy代理层,无需额外部署,无其他依赖,DBA也无需改变原有的运维方式。
Sharding-JDBC分片策略灵活,可支持等号、between、in等多维度分片,也可支持多分片键。
SQL解析功能完善,支持聚合、分组、排序、limit、or等查询,并支持Binding Table以及笛卡尔积表查询。
由图可以看出,我们只需要配置分片规则,然后关注业务就可以就可以实现数据库的分库分表,并不需要进行太多的配置
分片规则配置
Sharding-JDBC的分片逻辑非常灵活,支持分片策略自定义、复数分片键、多运算符分片等功能。
如:根据用户ID分库,根据订单ID分表这种分库分表结合的分片策略;或根据年分库,月份+用户区域ID分表这样的多片键分片。
Sharding-JDBC除了支持等号运算符进行分片,还支持in/between运算符分片,提供了更加强大的分片功能。
Sharding-JDBC提供了spring命名空间用于简化配置,以及规则引擎用于简化策略编写。由于目前刚开源分片核心逻辑,这两个模块暂未开源,待核心稳定后将会开源其他模块。
JDBC规范重写
Sharding-JDBC对JDBC规范的重写思路是针对DataSource、Connection、Statement、PreparedStatement和ResultSet五个核心接口封装,将多个真实JDBC实现类集合(如:MySQL JDBC实现/DBCP JDBC实现等)纳入Sharding-JDBC实现类管理。
Sharding-JDBC尽量最大化实现JDBC协议,包括addBatch这种在JPA中会使用的批量更新功能。但分片JDBC毕竟与原生JDBC不同,所以目前仍有未实现的接口,包括Connection游标,存储过程和savePoint相关、ResultSet向前遍历和修改等不太常用的功能。此外,为了保证兼容性,并未实现JDBC 4.1及其后发布的接口(如:DBCP 1.x版本不支持JDBC 4.1)。
SQL解析
SQL解析作为分库分表类产品的核心,性能和兼容性是最重要的衡量指标。目前常见的SQL解析器主要有fdb/jsqlparser和Druid。Sharding-JDBC使用Druid作为SQL解析器,经实际测试,Druid解析速度是另外两个解析器的几十倍。
目前Sharding-JDBC支持join、aggregation(包括avg)、order by、 group by、limit、甚至or查询等复杂SQL的解析。目前不支持union、部分子查询、函数内分片等不太应在分片场景中出现的SQL解析。
SQL改写
SQL改写分为两部分,一部分是将分表的逻辑表名称替换为真实表名称。另一部分是根据SQL解析结果替换一些在分片环境中不正确的功能。这里具两个例子:
第1个例子是avg计算。在分片的环境中,以avg1 +avg2+avg3/3计算平均值并不正确,需要改写为(sum1+sum2+sum3)/(count1+count2+ count3)。这就需要将包含avg的SQL改写为sum和count,然后再结果归并时重新计算平均值。
第2个例子是分页。假设每10条数据为一页,取第2页数据。在分片环境下获取limit 10, 10,归并之后再根据排序条件取出前10条数据是不正确的结果。正确的做法是将分条件改写为limit 0, 20,取出所有前2页数据,再结合排序条件算出正确的数据。可以看到越是靠后的Limit分页效率就会越低,也越浪费内存。有很多方法可避免使用limit进行分页,比如构建记录行记录数和行偏移量的二级索引,或使用上次分页数据结尾ID作为下次查询条件的分页方式。
SQL路由
SQL路由是根据分片规则配置,将SQL定位至真正的数据源。主要分为单表路由、Binding表路由和笛卡尔积路由。
单表路由最为简单,但路由结果不一定落入唯一库(表),因为支持根据between和in这样的操作符进行分片,所以最终结果仍然可能落入多个库(表)。
Binding表可理解为分库分表规则完全一致的主从表。举例说明:订单表和订单详情表都根据订单ID作为分片键,任意时刻分片逻辑均相同。这样的关联查询和单表查询难度和性能相当。
笛卡尔积查询最为复杂,因为无法根据Binding关系定位分片规则的一致性,所以非Binding表的关联查询需要拆解为笛卡尔积组合执行。查询性能较低,而且数据库连接数较高,需谨慎使用。
SQL执行
路由至真实数据源后,Sharding-JDBC将采用多线程并发执行SQL,并完成对addBatch等批量方法的处理。
结果归并
结果归并包括4类:普通遍历类、排序类、聚合类和分组类。每种类型都会先根据分页结果跳过不需要的数据。
普通遍历类最为简单,只需按顺序遍历ResultSet的集合即可。
排序类结果将结果先排序再输出,因为各分片结果均按照各自条件完成排序,所以采用归并排序算法整合最终结果。
聚合类分为3种类型,比较型、累加型和平均值型。比较型包括max和min,只返回最大(小)结果。累加型包括sum和count,需要将结果累加后返回。平均值则是通过SQL改写的sum和count计算,相关内容已在SQL改写涵盖,不再赘述。
分组类最为复杂,需要将所有的ResultSet结果放入内存,使用map-reduce算法分组,最后根据排序和聚合条件做相关处理。最消耗内存,最损失性能的部分即是此,可以考虑使用limit合理的限制分组数据大小。
结果归并部分目前并未采用管道解析的方式,之后会针对这里做更多改进
但是没有免费的午餐,BED对开发和维护有着一定的额外要求,而且这些要求都涉及面很广,绝对算得上伤筋动骨。开发层面包括:
- INSERT语句的主键不能自增
- UPDATE必须可重复执行,比如不支持UPDATE xxx SET x=x+1,对于更新余额类,这就相当于要求必须乐观锁了
运维层面包括:
- 需要存储事务日志的数据库
- 用于异步作业使用的zookeeper
- 解压sharding-jdbc-transaction-async-job-$VERSION.tar,通过start.sh脚本启动异步作业
我们选择了从设计层面避免强一致性的分布式事务。
分片灵活性
对于分库分表来说,很重要的一个特性是分片的灵活性,比如单个字段、多个字段的=、IN、>=、<=。为什么多个字段很重要的,这里涉及到一个特殊的考虑
sharding-jdbc目前提供4种分片算法。
由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。
- 精确分片算法
对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。
- 范围分片算法
对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。需要配合StandardShardingStrategy使用。
- 复合分片算法
对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,多分片键逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
- Hint分片算法(Hint分片指的是对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录ID分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。)
对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。
因为算法的灵活性,标准的方式是通过实现具体的java接口是实现具体的分片算法比如SingleKeyDatabaseShardingAlgorithm,有不少的情况下,分片是比较简单的,比如说纯粹是客户编号,此时提供了行内表达式分片策略,使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,不过这只支持单分片键。比如,t_user_${u_id % 8}
表示t_user表按照u_id按8取模分成8个表,表名称为t_user_0
到t_user_7
。
分片键+分片算法=真正可用的分片策略。
算法和分片键的选择是分库分表的关键,其直接决定了各个分库的负载是否均衡,以及扩展是否容易。在设计上的考虑一节笔者会详细阐述,订单和委托业务、用户在使用分库分表时设计上的考虑以及原因。
SQL语法限制
对于分库分表来说,还需要知道有哪些SQL的限制,尤其是涉及到需要二次处理的,比如排序,去重,聚合等。
这里笔者就列下那些常用但没有被支持的。比如:
- case when
- distinct
- union
不过好在这些在java/js中处理都比较方便。
如果有很复杂的SQL,那最大的可能就是设计上有问题,应该采用读写分离解决。
sharding-jdbc对SQL的限制完整可以参考http://shardingsphere.io/document/current/cn/features/sharding/usage-standard/sql/
设计上的考虑
哪些表要分库分表
首先从设计上要区分清楚哪些是广播表/哪些是分库表/哪些是只在一个库的全局表,因为是公用数据源的,所以不管是不是分库的表,都需要配置,不配置分片规则Sharding-JDB即无法精确的断定应该路由至哪个数据源。但是一般分库分表组件包括Sharding-JDBC都会提供简化配置的方法。对于不分片的表:
方法1:sharding-jdbc可以在<sharding:sharding-rule />配置default-data-source-name,这样未配置分片规则的表将通过默认数据源定位。
方法2:将不参与分库分表的数据源独立于Sharding-JDBC之外,在应用中使用多个数据源分别处理分片和不分片的情况。
分库还是分表
一般来说应该选择分库(准确的说是分schema),不应该使用分表, 因为oracle可以包含n个schema,mysql可以包含多个database,而且就算真的需要,schema之间也是可以关联查询的,所以感觉就算是为了扩展性问题,也没有必要使用分表,分表反而在扩展的时候更加麻烦。就算数据量多了一点,感觉稍微偏慢,可以先采用分区挡一挡。
分片键的选择
其中最重要的是分片键不能是自增字段,否则insert就不知道去哪里了。所以对于ID生成,需要一个ID生成中心。
分布式主键
分布式系统的主键生成可以通过设置增量或者也通过ID生成中心来生成,不过话说回来,既然使用ID生成中心了,就不要再使用数据库机制的ID了,这不一定需要通过代码全部重写,可以在dao层通过aop判断是否insert,是Insert的动态从ID中心获取,这样就避免了分库分表和非分库分表在开发商的差别。
mysql数据库分库分表shardingjdbc的更多相关文章
- php面试专题---mysql数据库分库分表
php面试专题---mysql数据库分库分表 一.总结 一句话总结: 通过数据切分技术将一个大的MySQLServer切分成多个小的MySQLServer,既攻克了写入性能瓶颈问题,同一时候也再一次提 ...
- 面试官:说说Mysql数据库分库分表,并且会有哪些问题?
之前一篇文章已经谈到了数据库集群之主从集群也就是读写分离,也提到了读写分离其实只是分担了访问的压力,但是存储的压力没有解决. 存储的压力说白了就是随着系统的演化,需求的增加,可能表的数量会逐渐增多,比 ...
- mysql数据库分库分表(Sharding)
mysql数据库切分 前言 通过MySQLReplication功能所实现的扩展总是会受到数据库大小的限制.一旦数据库过于庞大,尤其是当写入过于频繁,非常难由一台主机支撑的时候,我们还是会面临到扩展瓶 ...
- mysql数据库分库分表(Sharding)(转)
mysql数据库切分 前言 通过MySQLReplication功能所实现的扩展总是会受到数据库大小的限制.一旦数据库过于庞大,尤其是当写入过于频繁,非常难由一台主机支撑的时候,我们还是会面临到扩展瓶 ...
- MyBatis实现Mysql数据库分库分表操作和总结
前言 作为一个数据库,作为数据库中的一张表,随着用户的增多随着时间的推移,总有一天,数据量会大到一个难以处理的地步.这时仅仅一张表的数据就已经超过了千万,无论是查询还是修改,对于它的操作都会很耗时,这 ...
- MySQL+MyCat分库分表 读写分离配置
一. MySQL+MyCat分库分表 1 MyCat简介 java编写的数据库中间件 Mycat运行环境需要JDK. Mycat是中间件.运行在代码应用和MySQL数据库之间的应用. 前身 : cor ...
- 当当开源sharding-jdbc,轻量级数据库分库分表中间件
近期,当当开源了数据库分库分表中间件sharding-jdbc. Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据 ...
- sharding-jdbc,轻量级数据库分库分表中间件
Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据库分库分表访问.Sharding-JDBC是继dubbox和ela ...
- 【转】mysql分库分表,数据库分库分表思路
原文:https://www.cnblogs.com/butterfly100/p/9034281.html 同类参考:[转]数据库的分库分表基本思想 数据库分库分表思路 一. 数据切分 关系型数 ...
随机推荐
- NUnit属性
TestFixture:它标记一个类包含测试,申明该类是用来测试的.一般用在class的定义之前: Test一般是放在method之前,表示对该方法的测试:如前一篇文章所示的class. SetUp/ ...
- CSS秘密花园:多边框
今天在查询CSS3动画相关资料时偶然发现这个,感觉有时还是挺方便的.原文链接:http://www.w3cplus.com/css3/css-secrets/multiple-borders.html ...
- 辽宁工程技术大学校园网(深澜) 叠加小水管提速,多wan叠加负载均衡
教程没啥大用了.可以直接修改路由器为DHCP自动获取ip,然后直接登录校园网. 昨天晚上尝试了下用潘多拉固件多wan叠加网速,负载均衡,算是提高了速度. 转载请注明出处.教程供参考.本教程不是破解教程 ...
- 如何将Eclipse中的开源项目使用到Android Studio中
近几日,笔者用到了一些开源项目,比如著名的PTR项目.但是在使用的过程中,遇到了一些问题. 这个开源库是在Eclipse上面写的,我们现在开发用的是Android stuido. 两种软件的项目结构是 ...
- 面试题:JVM类加载机制详解(一)JVM类加载过程 背1
首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...
- 面向对象property属性、静态方法和类方法
一.property属性 1.什么是property特性? property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值. from math import pi class Circl ...
- c++虚析构函数的使用及其注意点
// ConsoleApplication33.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream& ...
- Mapper配置文件夹
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-// ...
- keydown、input 和 keyup 事件
keydown.keyup 属于键盘事件,input 属于文本事件. keydown:当用户按下键盘上的任意按键时触发,如果按住不放,会重复触发此事件. keyup:当用户释放键盘上的按键时触发. i ...
- HDU 6034 Balala Power! (贪心+坑题)
题意:给定一个 n 个字符串,然后问你怎么给 a-z赋值0-25,使得给定的字符串看成26进制得到的和最大,并且不能出现前导0. 析:一个很恶心的题目,细节有点多,首先是思路,给定个字符一个权值,然后 ...