Sharding-JDBC 系列
- 第一篇 Sharding-JDBC 源码之启动流程分析
- 第二篇 Sharding-JDBC 源码之 SQL 解析
- 第三篇 Sharding-JDBC 源码之 SQL 路由(本文)
- 第四篇 Sharding-JDBC 源码之 SQL 改写
- 第五篇 Sharding-JDBC 源码之 SQL 执行
- 第六篇 Sharding-JDBC 源码之结果集归并
接上文,在完成 SQL 解析之后,也就意味着我们拿到了逻辑 SQL 的抽象语法树及解析结果,
例如,以下SQL:
select goods_name from t_goods where goods_id=1 and type=0
解析之后的为抽象语法树见下图:
接下来,我们将继续学习SQL 路由过程。
路由类型
根据分片键划分路由
根据解析上下文匹配数据库和表的分片策略,生成路由路径。对于携带分片键的SQL,根据分片键的不同可以划分不同的路由:
- 单片路由:分片键的操作符是 =;
- 多片路由:分片键的操作符是 IN;
- 范围路由:分片键的操作符是 BETWEEN;
- 广播路由: 不携带分片键的SQL。
根据分片键场景划分路由
根据分片键进行路由的场景,又细分以下三种类型:
- 直接路由(强制路由):需使用HintAPI直接指定路由至库表,可适用分片键不在SQL中场景;
- 标准路由(推荐):适用范围是不包含关联查询或仅包含绑定表之间关联查询的SQL,在分片运算符是 BETWEEN 或 IN 时,单表查询或关联查询生成的真实SQL数量是一致的;
- 笛卡尔积路由:无法根据绑定表的关系定位分片规则,非绑定表之间的关联查询需要拆解为笛卡尔积组合执行。
不携带分片键的广播路由
不携带分片键的 SQL,则采取广播路由的方式,根据SQL类型又可以划分为以下5种:
- 全库表路由:处理对数据库中与其逻辑表相关的所有真实表的操作,主要包括不带分片键的 DQL 和 DML,以及 DDL 等;
- 全库路由:处理数据库相关命令,用于库设置的 SET 类型的数据库管理命令,以及 TCL 这样的事务控制语句;
- 全实例路由:用于 DCL 操作,授权语句针对的是数据库的实例;例如:
CREATE USER xxx
; - 单播路由:获取某一真实表信息的场景,它仅需要从任意库中的任意真实表中获取数据即可。例如:
DESCRIBE t_goods
; - 阻断路由:用于屏蔽SQL对数据库的操作,命令不会在真实数据库中执行例如:
USE order_db
。
SQL 路由
介绍完分片键类型,接下来我们从源码上来看是如何路由。以下继续以 select 查询为例,在上篇文章中,我们拿到了 SQLStatement
的实例 SelectStatement
对象,
在拿到 SelectStatement
对象后,开始进行路由,非 HintSQL 使用 ParsingSQLRouter
进行路由,执行 ParsingSQLRouter
#route 方法:
public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) {
GeneratedKey generatedKey = null;
// 如果是插入语句,获取生成的主键
if (sqlStatement instanceof InsertStatement) {
generatedKey = getGenerateKey(shardingRule, (InsertStatement) sqlStatement, parameters);
}
SQLRouteResult result = new SQLRouteResult(sqlStatement, generatedKey);
// 通过优化引擎获取分库分表条件,存放在 ShardingConditions 对象中
ShardingConditions shardingConditions = OptimizeEngineFactory.newInstance(shardingRule, sqlStatement, parameters, generatedKey).optimize();
// ......省略,先分析优化引擎
}
在上面代码片段中,优化引擎会去获取分库(表)的条件映射,看下 QueryOptimizeEngine#optimize
方法:
public ShardingConditions optimize() {
List<ShardingCondition> result = new ArrayList<>(orCondition.getAndConditions().size());
for (AndCondition each : orCondition.getAndConditions()) {
// 调用重载方法 optimize 封装 ShardingCondition 对象
// 1. each.getConditionsMap() 按照列进行分组
result.add(optimize(each.getConditionsMap()));
}
return new ShardingConditions(result);
}
// 重载方法 optimize 封装 ShardingCondition 对象
private ShardingCondition optimize(final Map<Column, List<Condition>> conditionsMap) {