mybatis流式查询大量mysql数据

/ 数据库 / 没有评论 / 450浏览

参数

fetchSize

Statement接口中是这样解释的:

为JDBC 驱动程序提供一个提示,它提示此Statement 生成的ResultSet 对象需要更多行时应该从数据库获取的行数。指定的行数仅影响使用此语句创建的结果集合。如果指定的值为 0,则忽略该提示。默认值为 0。

ResultSet中是这样解释的:

为 JDBC 驱动程序设置此ResultSet 对象需要更多行时应该从数据库获取的行数。如果指定的获取大小为零,则 JDBC 驱动程序忽略该值,随意对获取大小作出它自己的最佳猜测。默认值由创建结果集的Statement 对象设置。获取大小可以在任何时间更改。

网上有下面这样的一段摘录1:

缺省时,驱动程序一次从查询里获取所有的结果。这样可能对于大的数据集来说是不方便的, 因此 JDBC 驱动提供了一个用于设置从一个数据库游标抽取若干行的 ResultSet 的方法。在连接的客户端这边缓冲了一小部分数据行,并且在用尽之后, 则通过重定位游标检索下一个数据行块。

摘录2:

setFetchSize 最主要是为了减少网络交互次数设计的。访问ResultSet时,如果它每次只从服务器上取一行数据,则会产生大量的开销。setFetchSize的意 思是当调用rs.next时,ResultSet会一次性从服务器上取得多少行数据回来,这样在下次rs.next时,它可以直接从内存中获取出数据而不 需要网络交互,提高了效率。 这个设置可能会被某些JDBC驱动忽略的,而且设置过大也会造成内存的上升。

注:不同数据库实现不同,如mysql默认一次取回全部,如需生效则要使用下面的方式;

区别

1.常规查询,一次性读取 数据到 JVM 内存中,或者分页读取; 2.流式查询,建立长连接,利用服务端游标,每次读取一条加载到 JVM 内存;

流式查询有一点需要注意:必须先读取(或关闭)结果集中的所有行,
然后才能对连接发出任何其他查询,否则将引发异常;

使用流式查询,则要保持对产生结果集的语句所引用的表的并发访问,
因为其 查询会独占连接,所以必须尽快处理;

3.游标查询,和流式一样,通过 fetchSize 参数,控制一次读取多少条数据;

流式查询、游标查询可以避免 OOM,数据量大可以考虑此方案。
但是这两种方式会占用数据库连接,使用中不会释放,
所以线上针对大数据量业务用到游标和流式操作,一定要进行并发控制

适合场景

一次性查询大量不分页数据,使用后就释放,避免oom;如使用工具将数据写入excel;

编码方式

1.使用游标迭代器Cursor

@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
Cursor<WfGoodsOrderEntity> queryCursor();
Cursor<WfGoodsOrderEntity> cursor = orderDao.queryCursor();
LongAdder adder = new LongAdder();
cursor.forEach(wfGoodsOrderEntity -> {
             
});

注意,由于需要始终保持数据库连接,需要配合使用@Transactional

2.使用ResultHandler

@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
void queryCursor2(ResultHandler<WfGoodsOrderEntity>result);
<select id="queryCursor2" resultType="com.wfcm.entity.WfGoodsOrderEntity">
		select
			*
		from
			wf_goods_order
	</select>
orderDao.queryCursor2(resultContext -> {
       WfGoodsOrderEntity resultObject = resultContext.getResultObject();
});