shardingJdbc3.x 版本的分页bug问题解析

 更新时间:2023年06月02日 11:00:03   作者:雨翔河  
这篇文章主要为大家介绍了shardingJdbc3.x 版本的分页问题解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

shardingJdbc 改名为 shardingsphere ,同时项目也已经毕业并成为 Apache 顶级项目,但是这是发现它的第二个重大 BUG,说明还是有很大的进步空间。

上次发现的重大 BUG :https://www.jb51.net/program/286235e92.htm

BUG 的表象是在使用 shardingJdbc3.1.0 进行分页查询的时候存在问题,一般情况下 sql 是这样的格式 limit x,y ,但是不管怎么翻页查询,x 的值一直都是 0,这会导致查询出来的结果,每一次翻页的数据会越来越多,都包含了前一页的数据。

首先,使用 shardingJdbc 的情况下,理论上就不应该存在分页查询。 原因可以想象使用 shardingJdbc 是为了分表,既然是分表了那么这种查询必然会通过条件查询所有涉及到的表来得出结果,然后聚合到内存来进行分页,这会使得机器的压力是巨大的。

所以如果不涉及到分表的情况下,不应该是用 shardingJdbc,如果使用了分表就不应该这样搞分页查询。

前提条件是理想的情况下,但是在某些情况下,误用了导致没有分表策略的表使用了 shardingJdbc 来进行操作也是有可能的。

shardingJdbc3.1.0 在处理 sql 中含有 limit x,y 这种操作的时候会进行特殊处理。

类名: io.shardingsphere.core.routing.router.sharding.ParsingSQLRouter

格式化 SQL 和处理 limit 的操作

以下是截取的一段它的格式化 SQL 和处理 limit 的操作。

@Override
public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) {
    ......
    if (sqlStatement instanceof SelectStatement && null != ((SelectStatement) sqlStatement).getLimit()) {
        processLimit(parameters, (SelectStatement) sqlStatement);
    }
    ......
}
private void processLimit(final List<Object> parameters, final SelectStatement selectStatement) {
    boolean isNeedFetchAll = (!selectStatement.getGroupByItems().isEmpty() || !selectStatement.getAggregationSelectItems().isEmpty()) && !selectStatement.isSameGroupByAndOrderByItems();
    selectStatement.getLimit().processParameters(parameters, isNeedFetchAll, databaseType);
}

其中 selectStatement.getLimit () 得到的 Limit 类的实现有点问题,这个导致了问题就很大了。

类名: io.shardingsphere.core.parsing.parser.context.limit.Limit 

实现方法

以下是截取的一段它的实现方法

* @param parameters parameters
 * @param isFetchAll is fetch all data or not
 * @param databaseType database type
 */
public void processParameters(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType) {
    fill(parameters);
    rewrite(parameters, isFetchAll, databaseType);
}
private void fill(final List<Object> parameters) {
    int offset = 0;
    if (null != this.offset) {
        offset = -1 == this.offset.getIndex() ? getOffsetValue() : NumberUtil.roundHalfUp(parameters.get(this.offset.getIndex()));
        this.offset.setValue(offset);
    }
    int rowCount = 0;
    if (null != this.rowCount) {
        rowCount = -1 == this.rowCount.getIndex() ? getRowCountValue() : NumberUtil.roundHalfUp(parameters.get(this.rowCount.getIndex()));
        this.rowCount.setValue(rowCount);
    }
    if (offset < 0 || rowCount < 0) {
        throw new SQLParsingException("LIMIT offset and row count can not be a negative value.");
    }
}
private void rewrite(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType) {
    int rewriteOffset = 0;
    int rewriteRowCount;
    if (isFetchAll) {
        rewriteRowCount = Integer.MAX_VALUE;
    } else if (isNeedRewriteRowCount(databaseType)) {
        rewriteRowCount = null == rowCount ? -1 : getOffsetValue() + rowCount.getValue();
    } else {
        rewriteRowCount = rowCount.getValue();
    }
    if (null != offset && offset.getIndex() > -1) {
        parameters.set(offset.getIndex(), rewriteOffset);
    }
    if (null != rowCount && rowCount.getIndex() > -1) {
        parameters.set(rowCount.getIndex(), rewriteRowCount);
    }
}

在对 limit 语法进行处理的时候,会重写掉这个 offset,使得 offset=0,这就导致了每次向后翻页操作的数据会把前一次的分页查询结果也带上,每向后翻一页就会还是从 0 开始读取数据。

所以,解决方案就是如果不是分表就不要去用 shardingJdbc,如果是分表就不要去分页查询,性能损耗太严重,毕竟它得聚合到内存之后再进行分页处理,这个数据量过大的时候真的很容易出事。

如果一定要这么玩,那么看下 shardingJdbc 的新版本 4.x 是否修复了这个 BUG,听说已经修复了,没去试过。

以上就是shardingJdbc3.x 版本的分页问题解析的详细内容,更多关于shardingJdbc版本分页的资料请关注脚本之家其它相关文章!

相关文章

  • Springboot自动扫描包路径来龙去脉示例详解

    Springboot自动扫描包路径来龙去脉示例详解

    这篇文章主要介绍了Springboot自动扫描包路径来龙去脉示例详解,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Java实现登录密码强度校验的项目实践

    Java实现登录密码强度校验的项目实践

    本文主要介绍了Java实现登录密码强度校验的项目实践,包括使用正则表达式匹配校验和密码强度校验工具类这两种方法,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • IDEA报错:Process terminated的问题及解决

    IDEA报错:Process terminated的问题及解决

    这篇文章主要介绍了IDEA报错:Process terminated的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • spring-boot中spring-boot-maven-plugin报红错误及解决

    spring-boot中spring-boot-maven-plugin报红错误及解决

    这篇文章主要介绍了spring-boot中spring-boot-maven-plugin报红错误及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 详解java并发之重入锁-ReentrantLock

    详解java并发之重入锁-ReentrantLock

    这篇文章主要介绍了java并发之重入锁-ReentrantLock,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Java创建数组的几种方式总结

    Java创建数组的几种方式总结

    下面小编就为大家带来一篇Java创建数组的几种方式总结。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • SpringBoot Starter的工作原理解析

    SpringBoot Starter的工作原理解析

    SpringBootStarter是SpringBoot项目中用于简化配置的工具,通过自动配置和条件加载机制,它自动生成所需的组件和服务,减少了开发时间和配置工作,本文介绍SpringBoot Starter的工作原理,感兴趣的朋友一起看看吧
    2025-02-02
  • Springboot2以代码的方式统一配置Jackson教程

    Springboot2以代码的方式统一配置Jackson教程

    这篇文章主要介绍了Springboot2以代码的方式统一配置Jackson教程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java11中基于嵌套关系的访问控制优化详解

    Java11中基于嵌套关系的访问控制优化详解

    Java(和其他语言)通过内部类支持嵌套类,要使其正常工作,需要编译器执行一些技巧,下面这篇文章主要给大家介绍了关于Java11中基于嵌套关系的访问控制优化的相关资料,需要的朋友可以参考下
    2022-01-01
  • SpringBoot中的Aop用法示例详解

    SpringBoot中的Aop用法示例详解

    这篇文章主要介绍了SpringBoot中的Aop用法,本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12

最新评论