MyBatis Plus 解决大数据量查询慢问题
-
数据迁移 -
数据导出 -
批量处理数据
-
常规查询: 一次性读取 100w 数据到 JVM 内存中,或者分页读取 -
流式查询: 建立长连接,利用服务端游标,每次读取一条加载到 JVM 内存(多次获取,一次一行) -
游标查询: 和流式一样,通过 fetchSize 参数,控制一次读取多少条数据(多次获取,一次多行)
常规查询
public interface BigDataSearchMapper extends BaseMapper<BigDataSearchEntity> {@Select(“SELECT bds.* FROM big_data_search bds ${ew.customSqlSegment} “)
Page<BigDataSearchEntity> pageList(@Param(“page”) Page<BigDataSearchEntity> page, @Param(Constants.WRAPPER) QueryWrapper<BigDataSearchEntity> queryWrapper);
}
注:该示例使用的 MybatisPlus
流式查询
-
执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭。 -
必须先读取(或关闭)结果集中的所有行,然后才能对连接发出任何其他查询,否则将引发异常。
org.apache.ibatis.cursor.Cursor
的接口类用于流式查询,这个接口继承了 java.io.Closeable
和 java.lang.Iterable
接口,由此可知:-
Cursor 是可关闭的; -
Cursor 是可遍历的。
-
isOpen(): 用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据; -
isConsumed(): 用于判断查询结果是否全部取完。 -
getCurrentIndex(): 返回已经获取了多少条数据
sharding-sphere
的代码不难发现,除了group by
与order by
字段不一样之外,其他的场景都非常适合使用流式查询,可以最大限度的降低对客户端内存的消耗。fetchSize
的数据,直到把数据全部处理完。@Options
和 @ResultType
public interface BigDataSearchMapper extends BaseMapper<BigDataSearchEntity> {// 方式一 多次获取,一次多行
@Select(“SELECT bds.* FROM big_data_search bds ${ew.customSqlSegment} “)
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000000)
Page<BigDataSearchEntity> pageList(@Param(“page”) Page<BigDataSearchEntity> page, @Param(Constants.WRAPPER) QueryWrapper<BigDataSearchEntity> queryWrapper);
// 方式二 一次获取,一次一行
@Select(“SELECT bds.* FROM big_data_search bds ${ew.customSqlSegment} “)
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 100000)
@ResultType(BigDataSearchEntity.class)
void listData(@Param(Constants.WRAPPER) QueryWrapper<BigDataSearchEntity> queryWrapper, ResultHandler<BigDataSearchEntity> handler);
}
-
ResultSet.FORWORD_ONLY
:结果集的游标只能向下滚动 -
ResultSet.SCROLL_INSENSITIVE
:结果集的游标可以上下移动,当数据库变化时,当前结果集不变 -
ResultSet.SCROLL_SENSITIVE
:返回可滚动的结果集,当数据库变化时,当前结果集同步改变 -
fetchSize
:每次获取量
-
@ResultType(BigDataSearchEntity.class)
:转换成返回实体类型
注意:返回类型必须为 void ,因为查询的结果在 ResultHandler
里处理数据,所以这个 hander 也是必须的,可以使用 lambda 实现一个依次处理逻辑。
@Options
但实际操作却有不同:-
方式一是多次查询,一次返回多条; -
方式二是一次查询,一次返回一条;
fetch size
条记录放在客户端,客户端处理完成一个批次后再向服务器取下一个批次,直到所有数据处理完成。ResultSet.next()
方法时,会通过数据库连接一条一条的返回。flush buffer
的过程是阻塞式的,如果网络中发生了拥塞,send buffer
被填满,会导致 buffer 一直 flush 不出去,那 MySQL 的处理线程会阻塞,从而避免数据把客户端内存撑爆。-
非流式查询:内存会随着查询记录的增长而近乎直线增长。 -
流式查询:内存会保持稳定,不会随着记录的增长而增长。其内存大小取决于批处理大小 BATCH_SIZE
的设置,该尺寸越大,内存会越大。所以BATCH_SIZE应该根据业务情况设置合适的大小。
gxids.clear()
;微信赞赏支付宝扫码领红包
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。侵权投诉:375170667@qq.com