sharding-jdbc分库分表及读写分离

sharding-jdbc分库分表及读写分离

  • 介绍

sharding-jdbc定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

●适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。

●支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。

●支持任意实现 JDBC 规范的数据库,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。

sharding-jdbc分库分表及读写分离_第1张图片

 

鉴于其以jar包方式引入应用,配置简单,对开发友好以及无需额外部署等特点,同时其已在apache毕业,有理由相信未来越来越多系统可能会采用此技术来分库分表及读写分离。

上一篇的快速入门,并没有深入解析,本篇继续分享干货,深入了解执行原理,核心功能分库分表以及读写分离。

  • 执行原理

2.1 基本概念

在深入理解一种技术前,先了解以下基本概念。

逻辑表:

水平拆分表的总称,例如:借据表可以拆分成借据1表,借据2表等。但是借据表就是逻辑表,可理解为虚表。

真实表:

真实表即数据库中真实的表,为实际表。

数据节点:

简单理解为一个数据源,含库和表。

绑定表:

分片键一样的主子表,例如借据表的分片键是借据号,还款明细的分片键也是借据号,那么这两个表就是绑定表。

广播表:

见名知意,就是每个数据节点都需要的表,表结构和表中的数据在每个数据库中均完全一致。

分片键:

用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例如:将借据表中的主键借据id的尾数取模分片,则主键借据id为分片字段。 SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,Sharding- Jdbc也支持根据多个字段进行分片。

分片算法:

通过分片算法将数据分片,支持通过 = 、 BETWEEN 和 IN 分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。包括:精确分片算法 、范围分片算法 ,复合分片算法等。例如:where loan_id = ? 将采用精确分片算法,where loan_id in (?,?,?)将采用精确分片算法,where loan_id BETWEEN ? and ? 将采用范围分片算法,复合分片算法用于分片键有多个复杂情况。

分片策略:

包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。内置的分片策略大致可分为尾数取模、哈希、范围、标签、时间等。让用户方配置的分片策略则更加灵活,常用的使用行表达式配置分片策略,它采用Groovy表达式表示,如: s_user_$->{u_id % 8} 表示s_user 表根据u_id模8,而分成8张表,表名称为 s_user_0 到 s_user_7 。

自增主键生成策略:

通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。

2.2 SQL解析

当Sharding-JDBC接受到一条SQL语句时,会陆续执行 SQL解析 --> 查询优化 --> SQL路由 --> SQL改写 --> SQL执行 --> 结果归并,最终返回执行结果。

SQL解析过程分为词法解析和语法解析。词法解析器用于将SQL拆解为不可再分的原子符号,称为Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。再使用语法解析器将SQL转换为抽象语法树。

例如:

解析之后的抽象语法树如下:

sharding-jdbc分库分表及读写分离_第2张图片

 最后,通过对抽象语法树的遍历去提炼分片所需的上下文,并标记有可能需要SQL改写的位置。 供分片使用的解析上下文包含查询选择项(Select Items)、表信息(Table)、分片条件(Sharding Condition)、自增主键信息(Auto increment Primary Key)、排序信息(Order By)、分组信息(Group By)以及分页信息(Limit、Rownum、Top)。

2.3 SQL路由

SQL路由就是把针对逻辑表的数据操作映射到对数据结点操作的过程。 根据解析上下文匹配数据库和表的分片策略,并生成路由路径。 对于携带分片键的SQL,根据分片键操作符不同可以划分为单片路由(分片键的操作符是等号)、多片路由(分片键的操作符是IN)和范围路由(分片键的操作符是 BETWEEN),不携带分片键的SQL则采用广播路由。根据分片键进行路由的场景可分为直接路由、标准路由、笛卡尔路由等。

标准路由是Sharding-Jdbc最为推荐使用的分片方式,它的适用范围是不包含关联查询或仅包含绑定表之间关联查询的SQL。 当分片运算符是等于号时,路由结果将落入单库(表),当分片运算符是BETWEEN或IN时,则路由结果不一定落入唯一的库(表),因此一条逻辑SQL最终可能被拆分为多条用于执行的真实SQL。 举例说明,如果按照 user_id 的奇数和偶数进行数据分片,一个单表查询的SQL如下:

 

路由的结果为:

sharding-jdbc分库分表及读写分离_第3张图片

 

绑定表的关联查询与单表查询复杂度和性能相当,其余路由场景感兴趣的同学自行查阅资料。

2.4 SQL改写

工程师面向逻辑表书写的SQL,并不能够直接在真实的数据库中执行,SQL改写用于将逻辑SQL改写为在真实数据库中可以正确执行的SQL。

如一个简单的例子,若逻辑SQL为:

 

假设该SQL配置分片键user_id,并且user_id=2的情况,将路由至分片表2。那么改写之后的SQL应该为:

 

PS:还有一种group by和order by的情况sharding-jdbc会进行补列改写SQL。

2.5 SQL执行

Sharding-JDBC采用一套自动化的执行引擎,负责将路由和改写完成之后的真实SQL安全且高效发送到底层数据源执行。 它不是简单地将SQL通过JDBC直接发送至数据源执行;也并非直接将执行请求放入线程池去并发执行。它更关注平衡数据源连接创建以及内存占用所产生的消耗,以及最大限度地合理利用并发等问题。

2.6 结果归并

将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,称为结果归并。 Sharding-JDBC支持的结果归并从功能上可分为遍历、排序、分组、分页和聚合5种类型,它们是组合而非互斥的关系。

2.7 总结

通过以上内容介绍,大家已经了解到Sharding-JDBC基础概念、核心功能以及执行原理。 基础概念:逻辑表,真实表,数据节点,绑定表,广播表,分片键,分片算法,分片策略,主键生成策略核心功能:数据分片,读写分离。执行流程: SQL解析 --> 查询优化 --> SQL路由 --> SQL改写 --> SQL执行 --> 结果归并。接下来我们将通过一个个demo,来演示Sharding-JDBC实际使用方法。

  • 分库分表

水平分表:简单理解在多个数据库中复制一摸一样的表,或者一个数据库中复制几个一摸一样的表。在快速入门里,已经对水平分表进行实现,这里不再重复介绍。

水平分库:,水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器 上。接下来看一下如何使用Sharding-JDBC实现水平分库。

  1. 分片规则修改

由于数据库拆分了两个,这里需要配置两个数据源。 分库需要配置分库的策略,和分表策略的意义类似,通过分库策略实现数据操作针对分库的数据库进行操作。例如下的数据源配置,m0和s0配置了主从同步,为后续读写分离准备,m1和m2配置为水平分库。

sharding-jdbc分库分表及读写分离_第4张图片

 

如下是分库分表策略配置:

#分库策略,如何将一个逻辑表映射到多个数据源

spring.shardingsphere.sharding.tables.<逻辑表名称>.database‐strategy.<分片策略>.<    分片策略属性名>

#分表策略,如何将一个逻辑表映射为多个实际表

spring.shardingsphere.sharding.tables.<逻辑表名称>.table‐strategy.<分片策略>.<分         片策略属性名>

  1. 修改插入借据表数据
  2. sharding-jdbc分库分表及读写分离_第5张图片

 

截取部分打印日志

sharding-jdbc分库分表及读写分离_第6张图片

 

通过日志可以看出,根据user_id的奇偶不同,数据分别落在了不同数据源,达到目标。同时根据雪花算法得到的loan_id奇偶不同,数据均分在同一个库的不同表。实现了水平分库分表。

垂直分库:是指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念是专库专用。参照水平分库分表即可。

  • 公共表

公共表属于系统中数据量较小,变动少,而且属于高频联合查询的依赖表。参数表、数据字典表等属于此类型。可以将这类表在每个数据库都保存一份,所有更新操作都同时发送到所有分库执行。

  • 读写分离

5.1 读写分离介绍

面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。

通过一主多从的配置方式,可以将查询请求均匀的分散到多个数据副本,能够进一步的提升系统的处理能力。 使用多主多从的方式,不但能够提升系统的吞吐量,还能够提升系统的可用性,可以达到在任何一个数据库宕机,甚至磁盘物理损坏的情况下仍然不影响系统的正常运行。

读写分离的数据节点中的数据内容是一致的,而水平分片的每个数据节点的数据内容却并不相同。将水平分片和读写分离联合使用,能够更加有效的提升系统的性能。

5.2  mysql主从同步

采用docker搭建主从同步,具体搭建步骤略,如上分库分表部分的截图,m0和s0两个数据源组成一主一从。

5.3 实现sharding-jdbc读写分离

表结构如下:

sharding-jdbc分库分表及读写分离_第7张图片

 

1.在配置文件中修改

sharding-jdbc分库分表及读写分离_第8张图片

 

2.主从逻辑数据源定义

 

3.指定s_user表的数据分布情况

sharding-jdbc分库分表及读写分离_第9张图片

 

4.测试

sharding-jdbc分库分表及读写分离_第10张图片

sharding-jdbc分库分表及读写分离_第11张图片

 

 

5.结果,数据插入主库的两个表,如下图。

  1. sharding-jdbc分库分表及读写分离_第12张图片
  2. sharding-jdbc分库分表及读写分离_第13张图片 

 

6.查询,SQL发到从库去执行,并没有到主库m0上去。

sharding-jdbc分库分表及读写分离_第14张图片

 

 

六.总结

为什么要分库分表?分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。

最佳实践:系统在设计之初就应该对业务数据的耦合松紧进行考量,从而进行垂直分库、垂直分表,使数据层架构清晰明了。 若非必要,无需进行水平切分,应先从缓存技术着手降低对数据库的访问压力。如果缓存使用过后,数据库访问量还是非常大,可以考虑数据库读、写分离原则。若当前数据库压力依然大,且业务数据持续增长无法估量,最后可考虑水平分库、分表,单表拆分数据控制在1000万以内。

你可能感兴趣的:(sharding-jdbc,java,mysql,数据库,分布式)