Mybatis集成MySQL使用游标查询处理大批量数据方式
背景
基于数据的时间范围查询,给符合条件的用户推送积分即将到期的提醒。
初期用户量小使用最普通简单的分页查询扫描数据处理数据没问题。随着用户量的上升表数据已经上千万,每天扫描处理的数量也超百万,limit分页出现了慢sql,任务执行时间也达不到预期了。
上述方案出现瓶颈后考虑放弃limit方案,使用游标的方式进行全量数据的获取,这样一来SQL执行快任务执行也快。
MySQL游标查询
useCursorFetch
使用游标查询时,必须在jdbc url上设置连接属性参数useCursorFetch=true

FetchSize
在设置了useCursorFetch=true后,需要在SQL中指定fetchSize,即一次获取的数据量。
如果不设置fetchSize参数,则执行时仍然是全量返回,可能会出现OOM。
Mybatis集成Cursor查询
mapper接口方法返回值声明为Cursor类型,下面是SQL和Mapper的示例。
Cursor<Long> selectExpireCouponMember(@Param("endTime") String endTime);
<select id="selectExpireCouponMember" resultType="java.lang.Long" fetchSize="5000">
select
distinct member_id
from t_dj_coupon
where
end_time > end_time < #{endStartTime} and end_time < #{endTime}
</select>下面是基于上面的SQL做大量数据查询后写入文件的代码示例。
String fileName = DateFormatUtils.format(System.currentTimeMillis(), DateUtils.PATTERN_YYYY_MM_DD) + "_status_push_member.txt";
File file = new File(fileName);
file.createNewFile();
fileWriter = new FileWriter(file);
bufferedWriter = new BufferedWriter(fileWriter);
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
DjCouponMapper mapper = sqlSession.getMapper(DjCouponMapper.class);
try (Cursor<Long> cursor = mapper.selectStartCouponMember(startDate, startDateEnd, BrandContextHolder.getBrandMdCode())) {
Iterator<Long> iterator = cursor.iterator();
Set<Long> couponMemberSet = new HashSet<>(pageSize.intValue());
while (iterator.hasNext()) {
couponMemberSet.add(iterator.next());
writeNum++;
if (couponMemberSet.size() >= pageSize) {
bufferedWriter.write(couponMemberSet.toString());
bufferedWriter.newLine();
bufferedWriter.flush();
writeLine++;
couponMemberSet.clear();
}
}
if (CollectionUtils.isNotEmpty(couponMemberSet)) {
bufferedWriter.write(couponMemberSet.toString());
bufferedWriter.newLine();
bufferedWriter.flush();
writeLine++;
}
}
}Mybatis是如何实现基于Cursor查询的
com.mysql.cj.jdbc.result.ResultSetImpl实现类
ResultSetImpl 是mybatis中实现游标查询结果解析的类。
这个实现类的next方法中调用了ResultsetRows接口的next方法。

ResultsetRows接口
ResultsetRows接口有ResultsetRowsCursor,ResultsetRowsStatic,ResultsetRowsStreaming三个实现类。
本文写的游标查询的场景,使用的是ResultsetRowsCursor这个实现类。

详细看下ResultsetRowsCursor这个实现类,主要是实现了Iterator的hasNext和next方法,这也是使用Cursor获取数据需要的两个方法。
下面是hasnext方法的逻辑,根据下次要获取的游标索引和当前本地数据集计算返回是否还有后续数据可以获取。

下面是next方法的逻辑,先执行一下hasnext的逻辑判断,再取值

总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
一文搞懂Mysql中的共享锁、排他锁、悲观锁、乐观锁及使用场景
刚开始学习MySQL中锁的时候,网上一查出来一堆,什么表锁、行锁、读锁、写锁、悲观锁、乐观锁等等等,直接整个人就懵了,下面这篇文章主要给大家介绍了关于Mysql中共享锁、排他锁、悲观锁、乐观锁及使用场景的相关资料,需要的朋友可以参考下2022-07-07
SQL Server 完整备份遇到的一个不常见的错误及解决方法
这篇文章给大家介绍了SQL Server 完整备份遇到的一个不常见的错误及解决方法,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧2019-05-05
mysql和hive中几种关联(join/union)区别及说明
本文主要介绍了MySQL和Hive中各种连接和合并操作的用法,包括INNER JOIN、LEFT JOIN、RIGHT JOIN、LEFT SEMI JOIN以及UNION和UNION ALL,同时,也提到了Hive在处理连接时的一些特殊规则和限制2025-11-11


最新评论