流式查询1. mybatis的游标Cursor,分页大数据查询
流式查询流式查询 指的是查询成功后不是返回一个集合而是返回一个迭代器,应用可以通过迭代器每次取一条查询结果。流式查询的好处是能够降低内存使用。例如我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询。
而分页查询就需要我们按照顺序查询并设置一个参数来记录当前进度并在下次查询时将进度作为参数传入。
(比如按id升序查询,记录每次查询结果的最大id, 下次查询将这个最大id传入只查询大于这个id的),否则就会出现深度分页的情况。而查询效率取决于表设计,如果设计的不好,那么每次查询都会是一次单独的低效查询。
而流式查询不需要自己记录进度(数据库来记录),且即使表设计的较差或在sql比较复杂,也仅仅只需要一次低效查询。
流式查询的过程当中,要保证数据库连接是保持打开状态的,否则就会流关闭。
MyBaits通过游标Cursor实现了流式查询。 MyBaits Plus基于Mybais, 自然也是支持的。
如何使用
写一个获取流的Mapper
不需要其他配置, 像平常我们写查询一样在Mapper定义查询,并将返回结果设为Cursor即可实现一个流式查询。
sql也正常按照时间查询条件写,不需要加limit之类的参数。
1. 配置游标分页
在MyBatis Plus中,使用游标分页需要在mybatis-config.xml或application.yml 中添加配置:
mybatis-plus:
configuration:
settings:
useCursorFetch: true
@Transactional
@Async("asyncServiceExecutor")
public void asyncSaveAndUpdateCardPool(String buId, CardStatusReqVO cardReqVO) {
try (ShardingCtx s = ShardingCtx.setShardingValue(buId)) {
log.info("asyncSaveAndUpdateCardPool: {}, {}", buId, cardReqVO);
try (Cursor<CardUpdateVO> cursor = memberCardRepository.getCardCursorByBatchIdOrCardNum(
cardReqVO.getBatchId(),
cardReqVO.getStartingNumber(),
cardReqVO.getEndingNumber(),
cardReqVO.getCardRange())) {
final int BATCH_SIZE = 1000;
AtomicBoolean isFirstIteration = new AtomicBoolean(true);
CopyOnWriteArrayList<CardUpdateVO> updateVOList = new CopyOnWriteArrayList<>();
for (CardUpdateVO cardUpdateVO : cursor) {
updateVOList.add(cardUpdateVO);
if (updateVOList.size() == BATCH_SIZE) {
batchUpdateCardStatusAsync(buId, updateVOList, cardReqVO, isFirstIteration);
updateVOList.clear();
}
}
if (!updateVOList.isEmpty()) {
batchUpdateCardStatusAsync(buId, updateVOList, cardReqVO, isFirstIteration);
}
} catch (Exception e) {
log.error("Cursor process error: ", e);
}
}
log.info("Finished asyncSaveAndUpdateCardPool for cardReqVO: {}, {}", buId, cardReqVO);
}
接口:
Cursor<CardUpdateVO> getCardCursorByBatchIdOrCardNum(String batchId, String startingNumber, String endingNumber, String cardRange);
实现类:
@Override
public Cursor<CardUpdateVO> getCardCursorByBatchIdOrCardNum(String batchId, String startingNumber, String endingNumber, String cardRange) {
return customerLoyCardMapper.getCardCursorByBatchIdOrCardNum(batchId, startingNumber, endingNumber, cardRange);
}
mapper:
Cursor<CardUpdateVO> getCardCursorByBatchIdOrCardNum(@Param("batchId")String batchId,
@Param("startingNumber")String startingNumber,
@Param("endingNumber")String endingNumber,
@Param("cardRange")String cardRange);
sql:
<select id="getCardCursorByBatchIdOrCardNum" resultType="com.aswatson.csc.member.req.CardUpdateVO" resultSetType="FORWARD_ONLY" fetchSize = "500">
SELECT M.BU_ID as buId,
M.MEMBER_ID as memberId,
M.PROGRAM_ID as programId,
C.CARD_ID as cardId,
C.CARD_NUM as cardNum,
C.VISIBLE_CARD as visibleCard,
C.CARD_TYPE_CD as cardTypeCd,
C.STATUS_CD as statusCd,
C.ACTIVE_FLAG as activeFlag
FROM CUSTOMER_LOY_CARD C, CUSTOMER_LOY_MEMBER M
WHERE C.MEMBER_ID = M.MEMBER_ID
<if test="startingNumber != null and endingNumber != null and cardRange == 1 ">
AND C.VISIBLE_CARD BETWEEN #{startingNumber} AND #{endingNumber}
</if>
<if test="batchId != null and batchId != '' and cardRange == 2 ">
AND C.BATCH_ID = #{batchId}
</if>
</select>
需要注意的点:
获取一个打开状态的连接。
上面提过,使用流式查询需要保证数据库连接是保持打开状态。而正常情况下我们使用mybaits执行一次查询,连接都会被关闭或在重置。因此我们需要一些方法来保持连接。
使用事务:事务执行完毕之前连接会一直保持因此,我们可以来使用事务来保持连接。
这是最简单的方法,但是需要注意的是,由于cursor在遍历结束的方法末尾后会主动关闭连接。因此:
1. 方法内事务是正常的,在cursor查询前和遍历后的的数据操作依然是一个事务。从这里可以猜测,连接并不在在遍历完立即关闭,而是采用了类似AOP的手段在方法末尾关闭。
2. 如果你使用了数据库连接池,那么池中的这个连接会被关掉,这可能并不会导致你的连接池数量减少,因为连接池的连接数据依赖于各自的规则,
当有连接断开,连接池会根据自己的策略对做相应的处理,比如重新建立,因此不需要过分关注这个,需要注意的是这个行为可能带来的影响,
比如druid连接池,druid在事务执行完毕后会进行连接的清理。但是这个连接在myDbTableMapper遍历完成时已经关闭,
就会导致日志打印java.sql.SQLException: No operations allowed after statement closed。这个问题不会带来数据影响,
但是依然属于一个错误,见github-druid-issues,在1.2.10, druid将这个日志改为debug级别进行了临时的屏蔽。其他连接池尚未进行测试。
循环获取数据
Cursor继承了迭代器,可以通过Cursor获取他的Iterator, 或者直接使用for循环来获取数据。
需要注意的是,Cursor在查询一瞬间数据就固化了,如果你先查询,再更新,再遍历,即使在同一个事务里面,获得的结果也是未更新的数据。
关闭流
cursor在遍历结束后会主动关闭连接。如果未遍历结束中途退出,可以调用cursor的close方法关闭连接。推荐finaly内总是调用close或者try(resoure)来保证连接总是被正常关闭。
在使用MyBatis Plus的游标分页时,有一些注意事项和建议:
数据库支持: 游标分页依赖数据库的游标支持,因此确保数据库支持游标分页功能。
分页大小: 需要根据实际情况设置合适的分页大小,过小可能导致频繁查询,过大可能失去游标分页的优势。
及时关闭游标: 使用Cursor时,确保在处理完数据后及时关闭游标,释放资源。
性能监控: 对于大数据量的场景,建议进行性能监控和测试,确保游标分页带来的性能提升符合预期。
合理使用缓存: 需要根据实际情况考虑是否使用缓存,以及如何合理使用缓存,以提高查询效率。
版本更新: MyBatis Plus的版本可能会更新,建议关注最新版本的特性和改进,以获取更好的支持和性能。
————————————————
流式查询1. mybatis的游标Cursor,分页大数据查询的更多相关文章
- SQL命令语句进行大数据查询如何进行优化
SQL 大数据查询如何进行优化? 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索 2.应尽量避免在 where 子句中对字段进行 null 值 ...
- Facebook 正式开源其大数据查询引擎 Presto
Facebook 正式宣布开源 Presto —— 数据查询引擎,可对250PB以上的数据进行快速地交互式分析.该项目始于 2012 年秋季开始开发,目前该项目已经在超过 1000 名 Faceboo ...
- mysql 5.7 innodb count count(*) count(1) 大数据 查询慢 耗时多 优化
原文:mysql 5.7 innodb count count(*) count(1) 大数据 查询慢 耗时多 优化 问题描述 mysql 5.7 innodb 引擎 使用以下几种方法进行统计效率差不 ...
- 比hive快10倍的大数据查询利器presto部署
目前最流行的大数据查询引擎非hive莫属,它是基于MR的类SQL查询工具,会把输入的查询SQL解释为MapReduce,能极大的降低使用大数据查询的门槛, 让一般的业务人员也可以直接对大数据进行查询. ...
- 海胜专访--MaxCompute 与大数据查询引擎的技术和故事
摘要:在2019大数据技术公开课第一季<技术人生专访>中,阿里巴巴云计算平台高级技术专家苑海胜为大家分享了<MaxCompute 与大数据查询引擎的技术和故事>,主要介绍了Ma ...
- SQL 大数据查询如何进行优化?
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而 ...
- Sentry 监控 - Discover 事件大数据查询分析引擎
系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...
- [saiku] 使用 Apache Phoenix and HBase 结合 saiku 做大数据查询分析
saiku不仅可以对传统的RDBMS里面的数据做OLAP分析,还可以对Nosql数据库如Hbase做统计分析. 本文简单介绍下一个使用saiku去查询分析hbase数据的例子. 1.phoenix和h ...
- 技术分享:如何用Solr搭建大数据查询平台
0×00 开头照例扯淡 自从各种脱裤门事件开始层出不穷,在下就学乖了,各个地方的密码全都改成不一样的,重要帐号的密码定期更换,生怕被人社出祖宗十八代的我,甚至开始用起了假名字,我给自己起一新网名”兴才 ...
- SQL大数据查询分页存储过程
最后一页分页一卡死,整个网站的性能都会非常明显的下降,不知道为啥,微软有这个BUG一直没处理好.希望SQL2012里不要有这个问题就好了. 参考代码如下: -- =================== ...
随机推荐
- 使用 Docker 部署 VS Code in The Browser
1)介绍 GitHub:https://github.com/coder/code-server 在日常学习工作中,Vscode 已成为我们首选的代码编辑器.然而,其局限性在于当我们从家到公司移动时, ...
- 2024-05-22:用go语言,你有一个包含 n 个整数的数组 nums。 每个数组的代价是指该数组中的第一个元素的值。 你的目标是将这个数组划分为三个连续且互不重叠的子数组。 然后,计算这三个子数
2024-05-22:用go语言,你有一个包含 n 个整数的数组 nums. 每个数组的代价是指该数组中的第一个元素的值. 你的目标是将这个数组划分为三个连续且互不重叠的子数组. 然后,计算这三个子数 ...
- 超详细--redis在Linux环境搭建主从复制
引言Redis是一个高性能的缓存中间件,一个Redis服务器可以支撑很多的并发请求.但是在一些超高的并发场景下,虽然Redis读写速度很快,但也会产生读写压力过大,服务器负载过高的情况.为了分担读写的 ...
- rocketmq 搭建配置
broker组1: # NameServer地址 namesrvAddr=192.168.1.100: 9876;192.168.1.101: 9876 # 集群名称 brokerClusterNam ...
- 使用IDEA导入MyBatis源码进行调试
一. 下载源码 GitHub地址:https://github.com/mybatis/mybatis-3 复制上面的地址执行下列命令: git clone https://github.com/my ...
- Qt-FFmpeg开发-打开本地摄像头录制视频(7)
音视频/FFmpeg #Qt Qt-FFmpeg开发-打开本地摄像头录制视频[软解码+ OpenGL显示YUV] 目录 音视频/FFmpeg #Qt Qt-FFmpeg开发-打开本地摄像头录制视频[软 ...
- 我用docker搭建的第一个博客
其实很早就听说了docker这个东西,一直以来想玩不知道拿什么下手,再加上前段时间听了一个思科的年度网络报告讲解里面稍微提了一下docker的优势以及网络即服务的概念.想通了,不是每一步都得亲历亲为, ...
- 7.13早考试总结(NOIP模拟13)[工业题·卡常题·玄学题]
人的记忆本来就是暧昧的,不值得信任. 前言 又是令人头疼的数学部分..还是太菜了.. 晚上还有一场,当场裂开. T1 工业题 解题思路 首先,这个题的暴力还是非常好像的,直接按照题目要求码就好了. 对 ...
- JavaServlet和后端的搭建(以Tomcat为例)
目录 Servlet 前端如何才能访问到后端? 后端开发准备工作(配置Tomcat) 对象的生命周期 后端平台的搭建 创建Web项目(前提搭建好Tomcat配置) 创建Java文件 配置Web.xml ...
- 在 TypeScript 中,extends
extends 是一个关键字,用于指定类型参数的约束.它在类型参数的声明中使用,以确保类型参数满足特定的条件. 具体来说,extends 后面可以跟随一个类型,表示类型参数必须是该类型的子类型.在泛型 ...