Shardbatis的一些问题

公司项目压测期间,我负责的几个项目要配合支持,在联调过程中,发现一些shardbatis问题。


正文:

一、 项目一直用shardbatis做分表,这次压测的改动有涉及到mybatis,会新增加压测的mybatis插件。配置如下:

1
2
3
4
5
6
7
8
9
  <!-- 同时配置 压测插件 和 shardbatis插件 -->
<plugins>
<!-- 插件:分表 -->
<plugin interceptor="com.google.code.shardbatis.plugin.ShardPlugin">
<property name="shardingConfig" value="shard_config.xml" />
</plugin>
<!--插件:压测 -->
<plugin interceptor="com.xxx.pressure.interceptor.MybatisInterceptorXXX"/>
</plugins>

发现压测插件不生效,仔细看代码,它们都配置method = prepare ,导致插件冲突。

1
2
3
4
5
6
@Intercepts({@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class}
)})
public class ShardPlugin implements Interceptor {...}

所以解决办法及时 将两个插件整合在一起


二、 插件整合后,对项目进行压测。发现一些查询接口查不出数据,打印sql,发现sql中limit offset语句有问题。

一个原始的的sql语句:
SELECT id FROM user_info WHERE age > #{age} LIMIT #{pageSize} OFFSET #{limitStart}

最终执行的却是:
SELECT id FROM user_info WHERE age > #{age} LIMIT #{pageSize} , #{limitStart}

这样肯定查不出数据,囧,插件的问题反馈给插件开发的同学,最后得到的回复是:

他们使用了druid的开源sql修改工具,不支持limit offset,所以建议原始sql不要用limit y offset x,要求使用 limit x,y

到底是不是druid的锅,我没有验证,但既然他们要求改,那就改吧。但又引发了第三个问题。


三、 按照建议,原始sql改成了 limit x,y 的语法,压测流量没问题,回归测试时,正常流量却出现 和 2 一样的问题:一些查询接口查不出数据。哦豁这就有意思了。老办法打出执行的sql。

发现 一个原始的的sql语句:
SELECT id FROM user_info WHERE age > #{age} LIMIT #{limitStart} , #{pageSize}

最终执行的却是:
SELECT id FROM user_info WHERE age > #{age} LIMIT #{limitStart} OFFSET #{pageSize}

正常流量不会经过压测插件的修改流程,只会走shardbatis的修改流程。shardbatis 使用的是 net.sf.jsqlparser开源工具修改sql

1
2
3
4
5
 <dependency>
<groupId>net.sf.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.8.0</version>
</dependency>

追溯源码,找到net.sf.jsqlparser.statement.select.Limit ,发现实现如下

1
2
3
4
5
6
7
8
9
10
public String toString() {
String retVal = "";
if (rowCount > 0 || rowCountJdbcParameter ) {
retVal += " LIMIT "+(rowCountJdbcParameter?"?":rowCount+"");
}
if (offset > 0 || offsetJdbcParameter) {
retVal += " OFFSET "+(offsetJdbcParameter?"?":offset+"");
}
return retVal;
}

最后输出的sql会执行tostring()方法,这里可以看到,最后只会有两种结果limit x 或者 limit x offset y

所以解决方法就有两种:

  • 硬编码,在业务代码里区分压测流量和正常流量,如果是压测流量就走压测sql:limit x ,y ;如果是正常流量就走正常sql:limit y offset x
  • 更换 net.sf.jsqlparser jar包,net.sf.jsqlparser最新的维护时间是2012年,实在太老旧了。在github上有一个活跃的 com.github.jsqlparser ,应该可以作为替换(但是我们没有采用,毕竟是基础又重要的组件,如果替换,需要花费大量时间精力验证能否正确解析修改sql)

其实google已经不再维护shardbatis了,也无法找到公开的shardbatis jar,只在github 上找到一个历史rep

0%