继续以模块中的为基础,剖析分库分表核心功能路由;
ShardingPreparedStatementTest.java中查询调用,即调用ShardingPreparedStatement中的方法,核心源码如下:
通过上面的源码可知,SQL查询几个核心:路由,执行和结果合并,这篇文章主要分析路由的实现;
路由选择
接下来分析下面这段代码是如何取得路由信息的:
说明:SQLRouter接口有两个实现类:DatabaseHintSQLRouter和ParsingSQLRouter,两者之间如何选择,源码如下:
DatabaseHintSQLRouter
所以,选择DatabaseHintSQLRouter的用法如下:
所以,Hint语法还是比较简单的。由于我们的测试用例没有使用Hint语法强制路由数据库&表,所以调用ParsingSQLRouter中的route()方法;
ParsingSQLRouter
ParsingSQLRouter路由核心源码如下:
简单路由or复杂路由
由上面这段代码可知,满足如下任意一个条件就走简单路由,否则为复杂路由,假设sharding规则如下:
是否只有一张表
说明:这个"一张表"并不是指SQL中只有一张表,而是有分库分表规则的表数量,例如AbstractShardingJDBCDatabaseAndTableTest中这段构造ShardingRule的源码,总计有三张表有配置规则:t_order,t_order_item,t_config。所以如果有这样的SQL:,只有t_order涉及sharding规则,t_status并不涉及,所以任然认为"只有一张表";
是否都是绑定表
说明:isAllBindingTables(tableNames)判断tableNames是否都属于绑定表,根据刚才那段构造ShardingRule的源码可知:,和互为绑定表,那么:这个SQL只有和两个表且互为绑定表,那么shardingRule.isAllBindingTables(tableNames)为true;
是否属于默认数据源
说明:如果SQL中涉及的表全部属于默认数据源中,那么无论多少张表,都认为isAllInDefaultDataSource()为true,即走简单路由。
表是否属于默认数据源的判断依据:不在中,如果是spring配置方式,则不在中。例如如下配置,和都有表规则,如果SQL中有这两张表的任何一张表,都认为不属于默认数据源:
简单路由
执行SQL:时,由于SQL中只有一个表(1 == tableNames.size()),所以路由引擎是SimpleRoutingEngine;核心源码如下:
数据源路由详细解读
由于数据源的sharding策略为:
where条件为,即where条件中有,根据取模路由策略,当为奇数时,数据源为;当为偶数时,数据源为;
表路由详细解读
表的sharding策略为:
where条件中有,根据取模路由策略,当为奇数时,表为;当为偶数时,表为;
结论:最终需要执行SQL的数据节点(DataNode,由数据源名称和数据表组成)的总个数为:路由到的数据源个数*路由到的实际表个数;
示例
下面给出几个路由示例,以便更好的理解分库分表路由规则:
示例1:where o.order_id=1001 AND o.user_id=10,user_id=10所以路由得到数据源为dataSource_jdbc_0; order_id=1001,路由得到实际表为t_order_1;那么最终只需在数据节点上执行即可
示例2:where o.order_id=1000,user_id没有值所以路由得到所有数据源dataSource_jdbc_0和dataSource_jdbc_1; order_id=1000,路由得到实际表为t_order_0;那么最终需在和两个数据节点执行;
示例3:where o.user_id=11,user_id=11所以路由得到数据源为dataSource_jdbc_1; order_id没有值所以路由得到实际表为t_order_0和t_order_1;那么最终需要在和两个数据节点执行即可;
复杂路由
首先构造这种复杂路由场景t_order和t_order_item分库分表且绑定表关系,加入一个新的分库分表t_config,执行的SQL为:
构造的这个SQL会走复杂路由的逻辑(不是DDL,有三张表,t_config和t_order/t_order_item不是绑定表关系,t_order/t_order_item有分库分表规则所以不属于默认数据源);
复杂路由引擎的核心逻辑就是拆分成多个简单路由,然后求笛卡尔积,复杂路由核心源码如下:
由上面源码分析可知,会分别对t_config和t_order构造简单路由(t_order_item和t_order是绑定关系,二者取其一即可);
t_config需要分库不需要分表(因为不涉及分表),所以t_config这个逻辑表的简单路由结果为:dataSource_jdbc_0.t_config和dataSource_jdbc_1.t_config,有2个数据节点;
t_order_item分库分表,且有请求参数user_id=10和order_id=1001,所以t_order_item的简单路由结果为:dataSource_jdbc_0.t_order_item_1,只有1个数据节点(user_id=10路由数据源dataSource_jdbc_0,order_id=1001路由表t_order_item_1)。
CartesianRoutingEngine
如上分析,求得简单路由结果集后,求笛卡尔积就是复杂路由的最终路由结果,笛卡尔积路由引擎CartesianRoutingEngine的核心源码如下:
时输出的结果如下,可以看到重写后的1条实际SQL:(t_order_item与t_order是绑定表,保持一致):
如果把SQL条件做如下调整:
这样的话:
t_config需要分库不需要分表(因为不涉及分表),所以t_config这个逻辑表的简单路由结果为:dataSource_jdbc_0.t_config和dataSource_jdbc_1.t_config,有2个数据节点;
t_order_item分库分表,但是只有请求参数order_id=1001,所以t_order_item的简单路由结果为:dataSource_jdbc_0.t_order_item_1和dataSource_jdbc_1.t_order_item_1,也有2个数据节点。
那么最终的路由结果如下,两个数据源dataSource_jdbc_0 和dataSource_jdbc_1 都会路由到:
领取专属 10元无门槛券
私享最新 技术干货