继上一次将分库分表的依赖由3.0.0.M3的版本替换为3.1.0以支持数据库域名中带下划线后,又遇到另一个坑。
3.1.0版本的依赖如下:
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
问题描述
什么问题呢,具体记不太清了,两个多月前同事遇到的问题,一直忘了整理,因为项目是我负责搭建的,所以跟进解决下。大概就是某一线上应用报错,查看报错日志及sql日志定位到是分页查询的问题。举个例子,若分页大小为10,第1次真正执行的分页查询sql为
select * from XX where XX limit 0 10;
第2次真正执行的分页查询sql为
select * from XX where XX limit 0 20;
......
第10次真正执行的分页查询sql为
select * from XX where XX limit 0 100;
offset始终为0。这导致后继操作执行了一批已经处理过的数据,导致重复处理,业务中在处理已处理过的数据时给予了报错。那是什么原因导致上述问题的产生呢?
问题分析
在解决上述问题时,我们应先了解一件事,我们在使用ShardingSphere进行数据分片时,sql的执行流程时pagehelper->shardinshagsphere->mysql数据库。也就是我们写的不分页查询的sql先被pagehelper改写为分页查询的sql,然后再被shardinshagsphere改写,最后在数据库执行改写后的sql。
有兴趣的可以看下shardingsphere的内核剖析
我们再来看下shardinshagsphere是如何改写的,有兴趣的可以看下shardingsphere的改写引擎。
通过以上,猜测可能是sharding-jdbc改写了offset和limit,改写并真正执行的分页查询sql为以下形式:
select * from XX where XX limit 0 (offset + limit);
debug源码看下是否真的如此,查看源码:
其中processParameters方法的作用是填充参数及重写limit,其参数List<Object> parameters中包含分页用的offset和limit,真正用于分页的参数也是用的这里的,该方法调用fill和rewrite方法。fill方法用于参数的填充,rewrite方法用于重写limit,可以看到该方法中将List<Object> parameters中的offset重置为0,limit置为offset+limit,与我们上述猜测一致。
这也是io.sharding与pagehelper一起使用时的一个坑,shardinshagsphere用的是3.1.0版本,pagehelper使用的依赖是
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
也就是使用的是5.1.4版本的pagehelper。
感觉3.1.0版本的shardinioshagsphere与任意版本的pagehelper搭配使用时都会出现这个问题。
解决方式
由于使用io.shardinioshagsphere已经遇到了两个坑,最后选择弃用,改用如下依赖:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
其路由时关于分页的代码如下:
可以看出,其不会每次重写offset和limit。实际证明,问题也得到了解决。