欢迎关注头条号:老顾聊技术

精品原创技术分享,知识的组装工

前言

在了解Sharding-JDBC的执行原理前,需要了解以下概念:

逻辑表

水平拆分的数据表的总称。例:订单数据表根据主键尾数拆分为10张表,分别是 t_order_0 、 t_order_1 到t_order_9 ,他们的逻辑表名为 t_order 。

真实表

在分片的数据库中真实存在的物理表。即上个示例中的 t_order_0 到 t_order_9 。

数据节点

数据分片的最小物理单元。由数据源名称和数据表组成,例: ds_0.t_order_0 。

绑定表

指分片规则一致的主表和子表。例如: t_order 表和 t_order_item 表,均按照 order_id 分片,绑定表之间的分区键完全相同,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。

举例说明,如果SQL为:

SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10,11);

不配置绑定表关系时,假设分片键 order_id 将数值10路由至第0片,将数值11路由至第1片,那么路由后的SQL应该为4条,它们呈现为笛卡尔积:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in(10, 11); SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in(10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in(10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in(10, 11);

配置绑定表关系后,路由的SQL应该为2条:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

广播表

指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。

分片键

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

分片算法

通过分片算法将数据分片,支持通过 =、>=、<=、>、<、BETWEEN 和 IN 分片。 分片算法需要应用方开发者自行实现,可实现的灵活度非常高。

目前提供4种分片算法。 由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。

对应 PreciseShardingAlgorithm,用于处理使用单一键作为分片键的 = 与 IN 进行分片的场景。需要配合 StandardShardingStrategy 使用。

对应 RangeShardingAlgorithm,用于处理使用单一键作为分片键的 BETWEEN AND、>、<、>=、<=进行分片的场景。需要配合 StandardShardingStrategy 使用。

对应 ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合 ComplexShardingStrategy 使用。

对应 HintShardingAlgorithm,用于处理使用 Hint 行分片的场景。需要配合 HintShardingStrategy 使用。

分片策略

包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 分片算法,也就是分片策略。目前提供 5 种分片策略。

对应 StandardShardingStrategy。提供对 SQL语句中的 =, >, <, >=, <=, IN 和 BETWEEN AND 的分片操作支持。StandardShardingStrategy 只支持单分片键,提供 PreciseShardingAlgorithm 和 RangeShardingAlgorithm 两个分片算法。

PreciseShardingAlgorithm 是必选的,用于处理 = 和 IN 的分片

RangeShardingAlgorithm 是可选的,用于处理 BETWEEN AND, >, <, >=, <=分片,如果不配置RangeShardingAlgorithm,SQL 中的 BETWEEN AND 将按照全库路由处理。

对应 ComplexShardingStrategy。复合分片策略。提供对 SQL 语句中的 =, >, <, >=, <=, IN 和 BETWEEN AND 的分片操作支持。 ComplexShardingStrategy 支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。

对应 InlineShardingStrategy。使用 Groovy 的表达式,提供对 SQL 语句中的 = 和 IN 的分片操作支持,只支持单分片键。 对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的 Java 代码开发,如: t_user_$->{u_id % 8} 表示 t_user 表根据 u_id 模 8,而分成 8 张表,表名称为 t_user_0 到 t_user_7。

对应 HintShardingStrategy。通过 Hint 指定分片值而非从 SQL 中提取分片值的方式进行分片的策略。

对应 NoneShardingStrategy。不分片的策略。

自增 主键生成策略

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

SQL解析

当Sharding-JDBC接受到一条SQL语句时,会陆续执行 SQL解析 => 查询优化 => SQL路由 => SQL改写 => SQL执行 =>

结果归并 ,最终返回执行结果。

jdbc分表原理(分库分表之Sharding-Jdbc前篇)(1)

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

例如,以下SQL:

SELECT id, name FROM t_user WHERE status = 'ACTIVE' AND age > 18

解析之后的为抽象语法树见下图:

jdbc分表原理(分库分表之Sharding-Jdbc前篇)(2)

为了便于理解,抽象语法树中的关键字的Token用绿色表示,变量的Token用红色表示,灰色表示需要进一步拆分。

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

SQL路由

SQL路由就是把针对逻辑表的数据操作映射到对数据结点操作的过程

根据解析上下文匹配数据库和表的分片策略,并生成路由路径。 对于携带分片键的SQL,根据分片键操作符不同可以划分为单片路由(分片键的操作符是等号)、多片路由(分片键的操作符是IN)和范围路由(分片键的操作符是BETWEEN),不携带分片键的SQL则采用广播路由。

根据分片键进行路由的场景可分为直接路由、标准路由、笛卡尔路由等。

标准路由

标准路由是Sharding-Jdbc最为推荐使用的分片方式,它的适用范围是不包含关联查询或仅包含绑定表之间关联查询的SQL。

当分片运算符是等于号时,路由结果将落入单库(表),当分片运算符是BETWEEN或IN时,则路由结果不一定落入唯一的库(表),因此一条逻辑SQL最终可能被拆分为多条用于执行的真实SQL。

举例说明,如果按照 order_id 的奇数和偶数进行数据分片,一个逻辑表查询的SQL如下:

SELECT * FROM t_order WHERE order_id IN (1, 2);

那么路由的结果应为:

SELECT * FROM t_order_0 WHERE order_id IN (1, 2); SELECT * FROM t_order_1 WHERE order_id IN (1, 2);

绑定表的关联查询与单表查询复杂度和性能相当。

举例说明,如果一个包含绑定表的关联查询的 SQL如下:

SELECT * FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE order_id IN (1, 2);

那么路由的结果应为:

SELECT * FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE order_id IN (1,2); SELECT * FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE order_id IN (1,2);

可以看到,SQL拆分的数目与单表是一致的。

笛卡尔路由

笛卡尔路由是最复杂的情况,它无法根据绑定表的关系定位分片规则,因此非绑定表之间的关联查询需要拆解为笛卡尔积组合执行

如果上个示例中的SQL并未配置绑定表关系,那么路由的结果应为:

SELECT * FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE order_id IN (1,2); SELECT * FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE order_id IN (1,2); SELECT * FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE order_id IN (1,2); SELECT * FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE order_id IN (1,2);

笛卡尔路由查询性能较低,需谨慎使用。

全库表路由

对于不携带分片键的SQL,则采取广播路由的方式。根据SQL类型又可以划分为全库表路由、全库路由、全实例路由、单播路由和阻断路由这5种类型。其中全库表路由用于处理对数据库中与其逻辑表相关的所有真实表的操作,

主要包括不带分片键的DQL(数据查询)和DML(数据操纵),以及DDL(数据定义)等。例如:

SELECT * FROM t_order WHERE good_prority IN (1, 10);

则会遍历所有数据库中的所有表,逐一匹配逻辑表和真实表名,能够匹配得上则执行。路由后成为

SELECT * FROM t_order_0 WHERE good_prority IN (1, 10); SELECT * FROM t_order_1 WHERE good_prority IN (1, 10); SELECT * FROM t_order_2 WHERE good_prority IN (1, 10); SELECT * FROM t_order_3 WHERE good_prority IN (1, 10);

SQL改写

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

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

SELECT order_id FROM t_order WHERE order_id=1;

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

SELECT order_id FROM t_order_1 WHERE order_id=1;

再比如,Sharding-JDBC需要在结果归并时获取相应数据,但该数据并未能通过查询的SQL返回。 这种情况主要是针对GROUP BY和ORDER BY

结果归并时,需要根据 GROUP BY 和 ORDER BY 的字段项进行分组和排序,但如果原始SQL的选择项中若并未包含分组项或排序项,则需要对原始SQL进行改写。

先看一下原始SQL中带有结果归并所需信息的场景

SELECT order_id, user_id FROM t_order ORDER BY user_id;

由于使用user_id进行排序,在结果归并中需要能够获取到user_id的数据,而上面的SQL是能够获取到user_id数据的,因此无需补列。

如果选择项中不包含结果归并时所需的列,则需要进行补列,如以下SQL:

SELECT order_id FROM t_order ORDER BY user_id;

由于原始SQL中并不包含需要在结果归并中需要获取的user_id,因此需要对SQL进行补列改写。

补列之后的SQL是:

SELECT order_id, user_id AS ORDER_BY_DERIVED_0 FROM t_order ORDER BY user_id;

SQL执行

Sharding-JDBC采用一套自动化的执行引擎,负责将路由和改写完成之后的真实SQL安全且高效发送到底层数据源执行。 它不是简单地将SQL通过JDBC直接发送至数据源执行;也并非直接将执行请求放入线程池去并发执行

结果归并

将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,称为结果归并。

Sharding-JDBC支持的结果归并从功能上可分为遍历、排序、分组、分页和聚合5种类型,它们是组合而非互斥的关系。

归并引擎的整体结构划分如下图:

jdbc分表原理(分库分表之Sharding-Jdbc前篇)(3)

结果归并从结构划分可分为流式归并、内存归并和装饰者归并。流式归并和内存归并是互斥的,装饰者归并可以在流式归并和内存归并之上做进一步的处理

总结

今天老顾介绍了Sharding-JDBC基础概念、核心功能以及执行原理

基础概念:逻辑表,真实表,数据节点,绑定表,广播表,分片键,分片算法,分片策略,主键生成策略

执行流程: SQL 解析 => 查询优化 => SQL路由 => SQL改写 => SQL执行 => 结果归并

后面的文章,老顾将介绍Sharding-jdbc的实战使用,谢谢!!!

---End---


老顾的微服务网关分享课程,请大家多多支持

推荐阅读

a、dubbo如何处理业务异常,这个一定要知道哦!

b、企业级SpringBoot应用多个子项目配置文件规划、多环境支持(一)

c、企业级SpringBoot应用多个子项目配置文件规划、多环境支持(二)

d、企业级SpringBoot应用多个子项目配置文件之配置中心(三)

e、利用阿里开源工具进行排查线上CPU居高问题

f、阿里二面:如何快速排查死锁?如何避免死锁?

g、微服务分布式架构中,如何实现日志链路跟踪?

h、网关如何聚合各个微服务的接口文档?

i、Kubernetes之POD、容器之间的网络通信

j、K8S中的Service的存在理由

k、企业微服务项目如何进入K8S的全过程

l、阿里开源项目Sentinel限流、降级的统一处理

m、大厂二面:Redis的分布式布隆过滤器是什么原理?

1基于RocketMq的SpringCloud Stream框架实战入门

2、如何搭建消息中间件应用框架之SpringCloud Stream

3面试必备:网关异常了怎么办?如何做全局异常处理?

4Gateway网关系列(二):SpringCloud Gateway入门实战,路由规则

5Gateway网关系列开篇:SpringCloud的官方网关Gateway介绍

6API网关在微服务架构中的应用,这一篇就够了

7学习Lambda表达式看这篇就够了,不会让你失望的哦(续篇)

8Lambda用在哪里?几种场景?

9、为什么会出现Lambda表达式,你知道吗?

10、不说“分布式事务”理论,直接上大厂阿里的解决方案,绝对实用

11、女程序员问到这个问题,让我思考了半天,Mysql的“三高”架构

12、大厂二面:CAP原则为什么只能满足其中两项?而不能同时满足

13、阿里P7二面:聊聊零拷贝的原理

14、秒杀系统的核心点都在这里,快来取

15、你了解如何利用token方式实现分布式Session吗?

16、Mysql索引结构演变,为什么最终会是那个结构呢?让你一看就懂

17、一场比赛涉及到的知识,用通俗易通的方式介绍并发协调

18、企业实战Redis全方面思考,你思考了吗?

19、面试题:Thread的start和run的区别

20、面试题:什么是CAS?CAS的作用以及缺点

21、如何访问redis中的海量数据?避免事故产生

22、如何解决Redis热点问题?以及如何发现热点?

23、如何设计API接口,实现统一格式返回?

24、你真的知道在生产环境下如何部署tomcat吗?

25、分享一线互联网大厂分布式唯一ID设计 之 snowflake方案

26、分享大厂分布式唯一ID设计方案,快来围观

27、你想了解一线大厂的分布式唯一ID生成方案吗?

28、你知道如何处理大数据量吗?(数据拆分篇)

29、如何永不迁移数据和避免热点? 根据服务器指标分配数据量(揭秘篇)

30、你知道怎么分库分表吗?如何做到永不迁移数据和避免热点吗?

31、你了解大型网站的页面静态化吗?

32、你知道如何更新缓存吗?如何保证缓存和数据库双写一致性?

33、你知道怎么解决DB读写分离,导致数据不一致问题吗?

34、DB读写分离情况下,如何解决缓存和数据库不一致性问题?

35、你真的知道怎么使用缓存吗?

36、如何利用锁,防止缓存击穿?重构思想的重要性

37、海量订单产生的业务高峰期,如何避免消息的重复消费?

38、你知道如何保障生产端100%消息投递成功吗?

39、微服务下的分布式session该如何管理?

40、阿里二面:filter、interceptor、aspect应如何选择?很多人中招

41、互联网架构重要组员CDN,很多高级开发都没有实操过,来看这里

42、阿里二面:CDN缓存控制原理,看看能不能难住你

43、SpringCloud Alibaba之Nacos多环境多项目管理

44、SpringCloud Alibaba系列之Nacos配置中心玩法

45、SpringCloud Alibaba之Nacos注册中心

46、SpringCloud Plus版本之SpringCloud Alibaba

47、SpringCloud Alibaba之Nacos集群、持久化

48、SpringCloud Alibaba之Nacos共享配置、灰度配置

49、SpringCloud Alibaba之Sentinel工作原理

50、SpringCloud Alibaba之Sentinel流控管理

51、SpringCloud Alibaba之Sentinel降级管理

52、SpringCloud Alibaba之Sentinel热点参数限流

53、SpringCloud Alibaba之Sentinel的API实战

,