mysql 流式查询_Mybatis流式查询避免OOM

前言

当指定查询数据过大时,我们一般使用分页查询的方式,一页一页的将数据放到内存处理。但有些情况不需要分页的方式查询数据,如果一下子将数据全部加载出来到内存中,很可能会发生OOM。这时我们可以使用流式查询解决问题。

非流式查询

为了更好的复现问题,将jvm参数,最大堆设置成212M。使用mysql数据库,表大小为730MB。

e597c0fe541f9d41d0cba754216d4eae.png

非流式查询表里所有数据代码

List infoPOs = infoMapper.selectList(new EntityWrapper<>());

通过查看idea控制台,很快出现了内存溢出。

b8b17c105534ec3a29545d3721d717b4.png

通过jconsole工具,查看内存使用情况

018cb6514ed820dc555cf259c1b09d41.png

在14.26,发现内存直接被释放了。

流式查询

流式查询表里所有数据代码

@Select("select * from t_iot")

@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)

@ResultType(InfoPO.class)

void selectAutoList(ResultHandler handler);

infoMapper.selectAutoList(resultContext -> {

resultContext.getResultObject();

});

通过查看idea控制台,程序运行正常

49796dcdf855d7343dfeb600ecdb8442.png

在通过jconsole工具,查看内存使用情况

5f0a7c4a1d7f41294ffc1ba7ca3ee538.png

发现内存消耗处于平稳状态。

流式查询原理

查看源码可知,我们使用流式查询时,必须要满足以下3个条件

/**

* 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));

}

resultSetConcurrency=ResultSet.CONCUR_READ_ONLY 设置只读结果集

resultSetType = ResultSetType.FORWARD_ONLY 设置结果集的游标只能向下滚动

fetchSize = Integer.MIN_VALUE 设置fetch size为int的最小值,这里和oracle/db2有区别.

Oracle/db2是从服务器一次取出fetch size 条记录放在客户端,客户端处理完成一个批次后再向服务器取下一个批次,直到所有数据处理完成。

mysql在执行ResultSet.next()方法时,会通过数据库连接一条一条的返回。MySQL按照自己的节奏不断的把buffer写回网络中。flush buffer的过程是阻塞式的,也就是说如果网络中发生了拥塞,send buffer被填满,会导致buffer一直flush不出去,那MySQL的处理线程会阻塞,从而避免数据把客户端内存撑爆。

设置三个参数之后,断点进入到了流式返回结果集ResultsetRowsStreaming。

5386cac1b12d85884aa5cad46cadb956.png

ResultSet数据返回的结果,对象有3种实现方式

13ebebbd31a76114b4d72f5f2486b2a3.png

ResultsetRowsStatic 静态结果集,默认的查询方式,普通查询

ResultsetRowsCursor 游标结果集,服务器端基于游标查询

ResultsetRowsStreaming 动态结果集,流式查询

查看ResultsetRowsStatic类注释

/**

* Represents an in-memory result set

*/

public class ResultsetRowsStatic extends AbstractResultsetRows implements ResultsetRows {

表示放在内存中的结果集。

查看ResultsetRowsStreaming类注释

/**

* Provides streaming of Resultset rows. Each next row is consumed from the

* input stream only on {@link #next()} call. Consumed rows are not cached thus

* we only stream result sets when they are forward-only, read-only, and the

* fetch size has been set to Integer.MIN_VALUE (rows are read one by one).

*

* @param

* ProtocolEntity type

*/

public class ResultsetRowsStreaming extends AbstractResultsetRows implements ResultsetRows {

提供了Resultset行的流。获取下一行都是从仅在{@link #next()}调用时输入流。因此不会缓存已使用的行。我们只在结果集只有前进、只读和时才流结果集获取大小已设置为整数。MIN_VALUE(逐个读取行)。

总结

之前使用过db2处理流式查询,设置的fetch size为100,没有问题。这次使用mysql刚开始时也设置的100,发现内存溢出了,后来在网上看到mysql流式获取数据的坑,debug进去果然没走到ResultsetRowsStreaming类,设置fetch size 参数为Integer.MIN_VALUE后,才进了ResultsetRowsStreaming类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值