Sharding-JDBC 源码之 SQL 改写

Sharding-JDBC 系列

  1. 第一篇 Sharding-JDBC 源码之启动流程分析
  2. 第二篇 Sharding-JDBC 源码之 SQL 解析
  3. 第三篇 Sharding-JDBC 源码之 SQL 路由
  4. 第四篇 Sharding-JDBC 源码之 SQL 改写(本文)
  5. 第五篇 Sharding-JDBC 源码之 SQL 执行
  6. 第六篇 Sharding-JDBC 源码之结果集归并

在完成 SQL 路由后,我们就会拿到实际要查询的数据源名称及实际表节点。这时候就需要对原逻辑 SQL 进行改写,将逻辑表名替换为真实表名,这一步 Sharding-JDBC 是由改写引擎 SQLRewriteEngine 来完成的。

在看具体源码之前,先来了解下 SQL 改写有哪些类型

改写类型

正确性改写
标识符改写

需要改写的标识符包括表名称、索引名称以及Schema名称。如逻辑 SQL 为:

select goods_name from t_goods where goods_id = 1;

当 goods_id = 1时,将会路由到 分片表 1,那么改写后的 SQL 为:

select goods_name from t_goods_1 where goods_id = 1;
补列

补列通常由三种情况导致:

  1. 需要在结果归并时获取数据,但该列数据并未从查询的 SQL 中返回,这种场景主要针对GROUP BYORDER BY。结果归并时需要根据 GROUP BYORDER BY的字段进行分组和排序,但如果原始SQL的选择项中若并未包含分组项或排序项,则需要对原始SQL进行改写。如逻辑 SQL 为:
select goods_name from t_goods order by goods_id;

由于原 SQL 中不包含归并结果时需要的 goods_id 列,因此需要改写补列,改写后的 SQL 为:

select goods_name, goods_id AS ORDER_BY_DERIVED_0 from t_goods order by goods_id;

补列只会补充缺失的列,不会全部补充。

  1. 在使用 AVG 聚集函数时,在分布式的场景中,使用avg1 + avg2 + avg3 / 3计算平均值并不正确,需要改写为 (sum1 + sum2 + sum3) / (count1 + count2 + count3)。 这就需要将包含AVG的SQL改写为SUMCOUNT,并在结果归并时重新计算平均值。如以下SQL:
SELECT AVG(price) FROM t_order WHERE user_id=1;

需要改写为:

SELECT COUNT(price) AS AVG_DERIVED_COUNT_0, SUM(price) AS AVG_DERIVED_SUM_0 FROM t_order WHERE user_id=1;

然后才能够通过结果归并正确的计算平均值。

  1. 在执行INSERT的SQL语句时,如果使用了Sharding-JDBC分布式自增主键的生成策略,则需要通过补列,让使用方无需改动现有代码,即可将分布式自增主键透明的替换数据库现有的自增主键,如原逻辑 SQL 为:
INSERT INTO t_goods (`goods_name`) VALUES ('番茄鸡蛋盖浇饭');

配置自增主键后,SQL 改写为:

INSERT INTO t_goods (`goods_name`, goods_id) VALUES ('番茄鸡蛋盖浇饭', xxxxx);

改写后的SQL将在INSERT FIELD和INSERT VALUE的最后部分增加主键列名称以及自动生成的自增主键值。上述SQL中的xxxxx表示自动生成的自增主键值。

  • 分页修正(略)
  • 批量拆分(略)
优化改写
  • 单节点优化(略)
  • 流式归并优化(略)

由于篇幅原因,其他改写类型本文暂不再介绍,官网介绍的很详细,请移步官网查看:内部剖析-改写引擎
难得的中文官网,大家不要错过。配张官网改写引擎结构图:
在这里插入图片描述

源码分析

从源码上,改写的代码主要在 rewrite 包中:
在这里插入图片描述

上一章中,拿到 routingResult 后,会初始化 SQL 改写引擎 SQLRewriteEngine

    public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) {
   
        // ...... 省略
        // 获取路由结果
        RoutingResult routingResult = route(parameters, sqlStatement, shardingConditions);
        // 1. 初始化改写引擎
        SQLRewriteEngine rewriteEngine = new SQLRewriteEngine(shardingRule, logicSQL, databaseType, sqlStatement, shardingConditions, parameters);
        // 2. 根据 tableUnits.size 判断是否单路由
        boolean isSingleRouting = routingResult.isSingleRouting();
        if (sqlStatement instanceof SelectStatement && null != ((SelectStatement) sqlStatement).getLimit()) {
   
            processLimit(parameters, (SelectStatement) sqlStatement, isSingleRouting);
        }
        
        // 3. 执行改写,生成 SQLBuilder 对象
        SQLBuilder sqlBuilder = rewriteEngine.rewrite(!isSingleRouting);
        for (TableUnit each : routingResult.getTableUnits().getTableUnits(
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值