Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[错误报告]: 流式查询失效问题 #6485

Open
3 tasks done
nestorbian opened this issue Sep 13, 2024 · 6 comments
Open
3 tasks done

[错误报告]: 流式查询失效问题 #6485

nestorbian opened this issue Sep 13, 2024 · 6 comments
Labels

Comments

@nestorbian
Copy link

确认

  • 我使用的版本是最新版, 并且使用插件确认过项目里无依赖版本冲突
  • 我已经在 issue 中搜索过, 确认问题没有被提出过
  • 我已经修改标题, 将标题中的 描述 替换为遇到的问题

当前程序版本

3.5.7

问题描述

描述:
流式查询使用com.baomidou.mybatisplus.core.mapper.BaseMapper#selectList(com.baomidou.mybatisplus.core.conditions.Wrapper<T>, org.apache.ibatis.session.ResultHandler<T>)方法未开启流式查询,而是一次性查询出所有匹配数据

重现步骤:
1.MySQL建表

create table employee
(
    id         bigint auto_increment
        primary key,
    FIRST_NAME char(20) not null,
    LAST_NAME  char(20) null,
    AGE        int      null,
    SEX        char     null,
    INCOME     float    null
);

2.插入10w条测试数据

DELIMITER //
CREATE PROCEDURE insert_employee_data()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= 100000 DO
INSERT INTO employee (FIRST_NAME, LAST_NAME, AGE, SEX, INCOME)
VALUES
(CONCAT('姓名', i), CONCAT('姓氏', i), FLOOR(RAND() * (50 - 20 + 1)) + 20, IF(MOD(i, 2) = 0, '女', '男'), FLOOR(RAND() * (10000 - 4000 + 1)) + 4000);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;

CALL insert_employee_data();

3.定义mapper(省略项目搭建过程)

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee>{
}

4.测试代码

@Test
void contextLoads() {

        employeeMapper.selectList(null, new ResultHandler<Employee>() {
            @Override
            public void handleResult(ResultContext<? extends Employee> resultContext) {
                System.err.println("resultCount:" + resultContext.getResultCount() + " resultObject" + resultContext.getResultObject());
            }
        });

    }

5.设置debug断点,断点打在org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.lang.String)方法第一行,查看rsw最里层的resultSet的rowData字段
image
通过rowData字段看出直接查询了10w数据出来,如果数据量过大易产生OOM

修复建议:
提供全局的fetchSize配置设置的地方,或者提供方法级别设置fetchSize

演示正确开启流式查询
1.在重现步骤基础上,关注源码DefaultSqlInjector注入SelectList步骤,找出com.baomidou.mybatisplus.core.injector.methods.SelectList#injectMappedStatement方法中用到fetchSize的地方,打上断点,当前mp版本没有设置fetchSize也就是null,debug时需要手动指定为-2147483648(mysql只有这个值才能开启流式查询,像神通数据库之类指定大于0的值)
1726208099623

2.查看复现步骤5的断点,当rowData等于ResultsetRowStreaming类型时为开启流式查询成功
1726208099630

详细堆栈日志

No response

@nieqiurong
Copy link
Contributor

mybatis-plus:
    configuration:
        default-fetch-size: 10

@nestorbian
Copy link
Author

mybatis-plus:
    configuration:
        default-fetch-size: 10

配置完重新测试是可以的,感谢回复
另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题

@elcnu986
Copy link

mybatis-plus:
    configuration:
        default-fetch-size: 10

配置完重新测试是可以的,感谢回复 另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题

你好,我在yml中添加了 default-fetch-size: 10
spring boot 和 mp 的版本如下

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.2</version>
		<relativePath/>
	</parent>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>3.0.3</version>
    </dependency>

使用流式查询,该处org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.lang.String) 还是返回了全部结果
image

@elcnu986
Copy link

mybatis-plus:
    configuration:
        default-fetch-size: 10

配置完重新测试是可以的,感谢回复 另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题

我这边只有配置上 fetchSize 为 int 最小值时,才会返回 流
@options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
不知道你是怎么在添加上述yml配置后,就可以返回流了?

@nestorbian
Copy link
Author

mybatis-plus:
    configuration:
        default-fetch-size: 10

配置完重新测试是可以的,感谢回复 另外能否在mp官网流式查询上特别说明此配置,防止其他开发者遇到相同不生效的问题

我这边只有配置上 fetchSize 为 int 最小值时,才会返回 流 @options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE) 不知道你是怎么在添加上述yml配置后,就可以返回流了?

我是配置的

 mybatis-plus:
     configuration:
         default-fetch-size: -2147483648

mysql只能这个值才能生效,一开始我是没发现有这个可以指定fetchSize的配置

@mystery4f
Copy link

这个主要问题是 mysql 与客户端的约定,写死的 Integer.MIN_VALUE 游标才生效,
com.mysql.cj.jdbc.StatementImpl#createStreamingResultSet 中:

    /**
     * We only stream result sets when they are forward-only, read-only, and the
     * fetch size has been set to Integer.MIN_VALUE
     *
     * @return true if this result set should be streamed row at-a-time, rather
     *         than read all at once.
     */
    protected boolean createStreamingResultSet() {
        return this.query.getResultType() == Type.FORWARD_ONLY && this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY
                && this.query.getResultFetchSize() == Integer.MIN_VALUE;
    }

即对应 mapper xml select 标签的 这些属性

resultOrdered="true" fetchSize="-2147483648" resultSetType="FORWARD_ONLY"

但其实 Oracle/db2 是支持 fetchSize 自定义设置的...,感觉 非 mysql 可以加点个注解,按需 配置?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants