关系型数据库在大于一定数据量的情况下性能会急剧下降。在面对互联网海量数据的情况时,所有数据都存于一张表,显然很容易会达到数据表可承受的数据量阈值。单纯分表虽然可以解决数据量过大导致检索变慢的问题,但无法解决高并发情况下访问同一个库,导致数据库响应变慢的问题。所以通常水平拆分都至少要采用分库的方式,以一并解决大数据量和高并发的问题。但分表也有不可替代的场景。最常见的分表需求是事务问题。同一个库则不需要考虑分布式事务问题,善于使用同库不同表可有效的避免分布式事务带来的麻烦。目前,强一致性的分布式事务由于性能问题,导致使用起来性能并不一定会比不分库分表快,因此采用最终一致性的分布式事务居多。
分库分表用于应对当前互联网常见的两个场景:大数据量 & 高并发。通常分为:垂直拆分 & 水平拆分。 垂直拆分是根据业务将一个库(表)拆分为多个库(表)。如:将经常和不经常访问的字段拆分至不同的库(表)中,与业务关系密切。 水平拆分是根据分片算法将一个库(表)拆分为多个库(表)。
Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,是继dubbox、elastic-job之后ddframe开源的第三个项目。
Sharding-JDBC直接分装jdbc协议,可理解为增强版的JDBC驱动,旧代码迁移成本几乎为零,定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,无proxy层。
主要包括以下特点:
可适用于任何基于java的ORM框架,如:JPA、Hibernate、Mybatis、Spring JDBC Template,或直接使用JDBC
可基于任何第三方的数据库连接池,如:DBCP、C3P0、Durid等
理论上可支持任意实现JDBC规范的数据库。目前仅支持mysql
分片策略灵活,可支持等号、between、in等多维度分片,也可支持多分片键。
SQL解析功能完善,支持聚合、分组、排序、limit、or等查询,并支持Binding Table以及笛卡尔积表查询。
性能高,单库查询QPS为原生JDBC的99.8%,双库查询QPS比单库增加94%。
架构图:
核心概念
LogicTable:数据分片的逻辑表,对于水平拆分的数据库(表)来说,是同一类表的总称。如:订单数据根据主键尾数拆分为10张表,分表是t order0到t order 9,他们的逻辑表名为t_order。
ActualTable:分片数据中真实存在的物理表。
DataNode:数据分片的最小单元,由数据源名称和数据表组成。如:ds 1.t order_0。
DynamicTable:逻辑表和物理表不一定需要在配置规则中静态配置。如,按照日期分片的场景,物理表的名称随着时间的推移会产生变化。
BindingTable:指在任何场景下分片规则均一致的主表和子表。例:订单表和订单项表,均按照订单ID分片,则此两张表互为BindingTable关系。BindingTable关系的多表关联查询不会出现笛卡尔积关联,查询效率将大大提升。
ShardingColumn:分片字段用于将数据库(表)水平拆分的字段。
ShardingAlgorithm:分片算法。
SQL Hint:对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。
更详细的介绍可以参考官网。
下面使用springboot整合sharding-jdbc简单实现分表分库加读写分离实现负载均衡的场景。
首先添加依赖:
上面关键的依赖是 sharding-jdbc-spring-boot-starter ,包含了sharding-jdbc的核心依赖。使用starter可以很方便的使用,只需要少量配置即可。
启动类不用改变,下面说下配置文件,分表分库加读写分离主要的工作都在配置文件实现,这是使用starter的好处。下面配置两个数据库,并定义数据源:
这两个库要作为主库来使用,但是第一步先实现分表分库,首先设置分库规则,所有库的order表都根据user_id字段分库:
然后定义分表规则,所有order表主键都为order_id:
可以看到根据order_id的值,可以插入到不同的order表。
这样分表分库的规则就建好了,非常简单。而且完全不影响业务代码。下面看一下实体类:
dao类:
service类:
控制器:
可以看到,业务代码完全和以前集成jpa的一模一样。下面建立两个库,然后在库中建立两个order表:
每个表包含三个字段:
这样数据库和代码都完成了,接下来启动项目并添加四组数据:
根据两个id,可以推算出添加到哪个库的哪个表,添加完成,查看添加的位置是否正确:
位置全部正确,下面执行查询
可以看到查询没有问题,其它的查询就不再一一列举。可以自己尝试。
下面开始增加读写分离的功能,给每个主库增加两个从库,db_master_0对应db_slave_0,db_slave_2,db_master_1对应db_slave_1,db_slave_3,下面看配置,首先增加四个库:
配置四个库的数据源:
设置主从关系,根据主库数量,分为db_0和db_1两个库:
分库规则中的名字要修改一下:
分表规则中数据库的名字也要修改一下:
注意:sharding-jdbc没有实现主从同步,需要自己在服务端配置!
配置好以后重启,清空所有库,从新执行上面四个接口,可以看到同样成功,此处不再截图。
接下来将从库中0库和2库的order0数据直接在数据库中修改一下,把name字段的master改成slave,然后访问这条数据:
可以看到系统不再访问主库,开始循环访问两个从库,实现了读写分离和负载均衡,默认是轮训的策略。
上面简单实现了分库分表和读写分离,其它配置和测试可以自己实验。
代码地址: https://gitee.com/blueses/spring-boot-demo