记录Sharding JDBC 不支持业务子查询问题的解决思路

使用版本

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.1</version>
</dependency>

参考文档

概览 :: ShardingSphere 

起因

公司的一些记录表数据量到达几千万,有些已经一个多亿了,领导让用Sharding按照年月搞一下分库分表,接到任务后根据官方文档和一些帖子,整合了一下,自测基础crud都没啥问题,笑眯眯提交需求工单,提测没多久,测试指给我几个bug工单,基本都是数据导出和统计查询。

跟代码,发现几个问题相同点是用到了子查询或者函数,查询时执行的sql并没有如预想当中,查询对应的月份表。

SQL

查询所用sql

SELECT
    * 
FROM
    obc_outbound_log
    INNER JOIN ( SELECT id FROM obc_outbound_log o WHERE ctime BETWEEN '2022-05-01 00:00:00' AND '2022-05-31 23:59:59' LIMIT 10 ) t ON obc_outbound_log.id = t.id

 执行的sql

SELECT * FROM obc_outbound_log_202205 inner join (SELECT id FROM obc_outbound_log o limit 10)t on obc_outbound_log.id=t.id where obc_outbound_log_202205.ctime between '2022-05-01T18:27:23.000+0800' and '2022-05-31T23:59:59.999+0800'
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Table 'obc_record.obc_outbound_log' doesn't exist 

注:(sql这么写是为了解决分页偏移量过大)

参考文章:Mysql中limit分页大偏移量的原因分析与优化_xiaopang小白的博客-CSDN博客_limit分页偏移量较大的情况下去优化

问题分析

子查询中的记录表,并没有替换表名,怀疑是SQL解析引擎,解析出的token有问题,去官网去找了一下资料,发现Sharding JDBC有很多不支持的sql语法。

SQL :: ShardingSphere

虽然官方说不支持,但是因为历史业务sql用到子查询的地方较多,挨个去改sql工作量就变得太大了, 领导让想办法兼容一下,既要分表,也要尽可能兼容一些子查询,结构特别复杂的可以先放一放。

然后就继续读文档,看博客得知,ShardingSphere 的三个产品对于分片的流程都是一样的

SQL解析 => 执行器优化 => SQL路由 => SQL改写 => SQL执行 => 结果归并

SQL解析

分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语法解析器对SQL进行理解,并最终提炼出解析上下文。 解析上下文包括表、选择项、排序项、分组项、聚合函数、分页信息、查询条件以及可能需要修改的占位符的标记。

执行器优化

合并和优化分片条件,如OR等。

SQL路由

根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。

SQL改写

将SQL改写为在真实数据库中可以正确执行的语句。SQL改写分为正确性改写和优化改写。

SQL执行

通过多线程执行器异步执行。

结果归并

将多个执行结果集归并以便于通过统一的JDBC接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。

执行流程

问题和猜想的差不多,就是出在解析和重写那了,跟源码看执行流程

 

 

 

处理方案

问题定位到了,开始想解决方案

1:要么从源头(SQL解析引擎)解决,重写方法,得到预想的SqlToken。

2:重写AbstractSQLBuilder这个方法,加入额外的业务逻辑。

 总结

        目前只是一个解决思路,直接使用肯定会有问题,比如错误替换了别的表名,例如test_table替换为test_table_202205,但where sql中还存在test_table_log、test_table_record会有问题,慢慢完善,思路有了,问题就好解决了。

        关于Sharding JDBC对于使用函数报错的问题,我是看的这篇博客

Sharding升级 4.0 版本升级到4.1 SQL 语法问题总结_魔力化的博客-CSDN博客

        有理解错误的地方,希望各位大佬指出。

 

 

 

 

 

 

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
ShardingJDBC 是一个基于 JDBCJava 框架,它可以将 SQL 解析和路由到不同的数据库实例中,并且支持水平扩展。进行条件查询时,可以使用 ShardingJDBC 提供的 API 或配置文件的方式来指定表的分片策略和对应的数据库实例,从而实现对分片表的查询操作。 下面是一个使用 ShardingJDBC 进行条件查询的示例: 1. 配置数据源和分片规则 ```yaml spring: sharding: jdbc: datasource: names: ds0,ds1 ds0: driver-class-name: com.mysql.jdbc.Driver jdbcUrl: jdbc:mysql://localhost:3306/db0?useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: root ds1: driver-class-name: com.mysql.jdbc.Driver jdbcUrl: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: root config: sharding: tables: user: actual-data-nodes: ds$->{0..1}.user_$->{0..2} table-strategy: inline: sharding-column: id algorithm-expression: user_$->{id % 3} key-generator: column: id type: SNOWFLAKE ``` 2. 进行条件查询 ```java @Autowired private DataSource dataSource; @Test public void testSelect() throws SQLException { String sql = "SELECT * FROM user WHERE id = ?"; try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setLong(1, 1); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { System.out.println(rs.getLong("id")); System.out.println(rs.getString("name")); } } } } ``` 在上述示例中,我们首先配置了两个数据源 ds0 和 ds1,然后指定了 user 表的分片策略和对应的数据库实例。最后,在进行条件查询时,我们使用了 ShardingJDBC 提供的 DataSource 来获取连接,并执行了相应的 SQL 语句。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值