MyBatis Plus 大数据量查询优化

大数据量操作的场景大致如下:

  • 数据迁移

  • 数据导出

  • 批量处理数据

在实际工作中当指定查询数据过大时,我们一般使用分页查询的方式一页一页的将数据放到内存处理。但有些情况不需要分页的方式查询数据或分很大一页查询数据时,如果一下子将数据全部加载出来到内存中,很可能会发生OOM(内存溢出);而且查询会很慢,因为框架耗费大量的时间和内存去把数据库查询的结果封装成我们想要的对象(实体类)。

举例:在业务系统需要从 MySQL 数据库里读取 100w 数据行进行处理,应该怎么做?

做法通常如下:

  • 常规查询: 一次性读取 100w 数据到 JVM 内存中,或者分页读取

  • 流式查询: 建立长连接,利用服务端游标,每次读取一条加载到 JVM 内存(多次获取,一次一行)

  • 游标查询: 和流式一样,通过 fetchSize 参数,控制一次读取多少条数据(多次获取,一次多行)

常规查询

默认情况下,完整的检索结果集会将其存储在内存中。在大多数情况下,这是最有效的操作方式,并且由于 MySQL 网络协议的设计,因此更易于实现。

举例:

假设单表 100w 数据量,一般会采用分页的方式查询:

@Mapper
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

该方式比较简单,如果在不考虑 LIMIT 深分页优化情况下,估计你的数据库服务器就噶皮了,或者你能等上几十分钟或几小时,甚至几天时间检索数据

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

流式查询

流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。

如果没有流式查询,我们想要从数据库取 100w 条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询。因此流式查询是一个数据库访问框架必须具备的功能。

MyBatis 中使用流式查询避免数据量过大导致 OOM ,但在流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:

  • 执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭。

  • 必须先读取(或关闭)结果集中的所有行,然后才能对连接发出任何其他查询,否则将引发异常。

MyBatis 流式查询接口

MyBatis 提供了一个叫 org.apache.ibatis.cursor.Cursor 的接口类用于流式查询,这个接口继承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知:

  • Cursor 是可关闭的;

  • Cursor 是可遍历的。

除此之外,Cursor 还提供了三个方法:

  • isOpen(): 用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据;

  • isConsumed(): 用于判断查询结果是否全部取完。

  • getCurrentIndex(): 返回已经获取了多少条数据

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

为什么要用流式查询?

如果有一个很大的查询结果需要遍历处理,又不想一次性将结果集装入客户端内存,就可以考虑使用流式查询;

分库分表场景下,单个表的查询结果集虽然不大,但如果某个查询跨了多个库多个表,又要做结果集的合并、排序等动作,依然有可能撑爆内存;详细研究了sharding-sphere的代码不难发现,除了group byorder by字段不一样之外,其他的场景都非常适合使用流式查询,可以最大限度的降低对客户端内存的消耗。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

游标查询

对大量数据进行处理时,为防止内存泄漏情况发生,也可以采用游标方式进行数据查询处理。这种处理方式比常规查询要快很多。

当查询百万级的数据的时候,还可以使用游标方式进行数据查询处理,不仅可以节省内存的消耗,而且还不需要一次性取出所有数据,可以进行逐条处理或逐条取出部分批量处理。一次查询指定 fetchSize 的数据,直到把数据全部处理完。

Mybatis 的处理加了两个注解:@Options 和 @ResultType

@Mapper
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);
 
}

@Options

  • ResultSet.FORWORD_ONLY:结果集的游标只能向下滚动

  • ResultSet.SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时,当前结果集不变

  • ResultSet.SCROLL_SENSITIVE:返回可滚动的结果集,当数据库变化时,当前结果集同步改变

  • fetchSize:每次获取量

@ResultType

  • @ResultType(BigDataSearchEntity.class):转换成返回实体类型

注意:返回类型必须为 void ,因为查询的结果在 ResultHandler 里处理数据,所以这个 hander 也是必须的,可以使用 lambda 实现一个依次处理逻辑。

注意:

虽然上面的代码中都有 @Options 但实际操作却有不同:

  • 方式一是多次查询,一次返回多条;

  • 方式二是一次查询,一次返回一条;

原因:

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

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

非流式查询和流式查询区别:

  • 非流式查询:内存会随着查询记录的增长而近乎直线增长。

  • 流式查询:内存会保持稳定,不会随着记录的增长而增长。其内存大小取决于批处理大小BATCH_SIZE的设置,该尺寸越大,内存会越大。所以BATCH_SIZE应该根据业务情况设置合适的大小。

另外要切记每次处理完一批结果要记得释放存储每批数据的临时容器,即上文中的gxids.clear();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/250435.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿里5年经验之谈 —— 浅谈自动化测试方法!

导读 在当今快节奏的软件开发环境中&#xff0c;高质量的代码交付至关重要。而针对经过多次迭代&#xff0c;主要功能趋向稳定的产品&#xff0c;大量传统的重复性手动测试方法已经无法满足高效、快速的需求。为了提高测试效率保证产品质量&#xff0c;本文通过产品实践应用&a…

LeetCode(66)二叉树的最大深度【二叉树】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 二叉树的最大深度 1.题目 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7]…

DS考研真题总结——客观题(1)

开始整理真题中的客观小题&#xff0c;至于和算法有关的大题统一最后整理~ 定义背诵&#xff1a;数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者存储效…

C语言----文件操作(二)

在上一篇文章中我们简单介绍了在C语言中文件是什么以及文件的打开和关闭操作&#xff0c;在实际工作中&#xff0c;我们不仅仅是要打开和关闭文件&#xff0c;二是需要对文件进行增删改写。本文将详细介绍如果对文件进行安全读写。 一&#xff0c;以字符形式读写文件&#xff…

JVM学习之JVM概述

JVM的整体结构 Hotspot VM是目前市面上高性能虚拟机代表作之一 它采用解释器与即时编译器并存的架构 在今天&#xff0c;Java程序的运行性能已经达到了可以和C/C程序一较高下的地步 Java代码执行流程 具体图为 JVM架构模型 Java编译器输入的指令流基本上是一种基于 栈的指令…

关于负载和驱动能力的问题总结

这两天重新接触到了驱动能力这个说法&#xff0c;之前也听过&#xff0c;但是一直不理解是怎么回事儿&#xff0c;也就没有深究&#xff0c;现在想来&#xff0c;这里面还是有点门道的。 驱动能力&#xff0c;说的是什么呢&#xff1f;应该就是带载能力&#xff0c;而带载能力&…

Linux 中的网站服务管理

目录 1.安装服务 2.启动服务 3.停止服务 4.重启服务 5.开机自启 6.案例 1.安装服务 网址服务程序 yum insatll httpd -y 查看所有服务 systemctl list-unit-files 2.启动服务 systemctl start httpd 查看服务进程&#xff0c;确认是否启动 ps -ef|grep httpd 3.停止…

Windows Linux - 关于IP地址看这一篇就够了

目录 &#x1f959;1 IP地址简介 &#x1f959;2 IP地址分类 &#x1f96a;分类方式1 &#x1f96a;分类方式2 &#x1f96a;特殊的IP地址 - 本机IP地址 &#x1f959;3 域名&#xff1a;便捷的记录IP地址 &#x1f959;4 常用命令 &#x1f959;5 查看网络IP和网关 &…

内网穿透工具,如何保障安全远程访问?

内网穿透工具是一种常见的技术手段&#xff0c;用于在没有公网IP的情况下将本地局域网服务映射至外网。这种工具的使用极大地方便了开发人员和网络管理员&#xff0c;使得他们能够快速建立起本地服务与外部网络之间的通信渠道。然而&#xff0c;在享受高效快捷的同时&#xff0…

windows电脑半夜突然睡眠自动唤醒的问题查找与治理

遇见几次了&#xff0c;半夜起来上厕所&#xff0c;发现休眠的电脑居然自己开了&#xff0c;还得跑过去把电脑再休眠&#xff0c;很烦。昨天晚上居然自动唤醒两次&#xff0c;忍无可忍了&#xff0c;于是开始查找原因。 查询原因如下&#xff0c;解决方面也在后面。 固件 S3 计…

深度学习记录--矩阵维数

如何识别矩阵的维数 如下图 矩阵的行列数容易在前向和后向传播过程中弄错&#xff0c;故写这篇文章来提醒易错点 顺便起到日后查表改错的作用 本文仅作本人查询参考(摘自吴恩达深度学习笔记)

Python学习路线 - Python语言基础入门 - 数据容器

Python学习路线 - Python语言基础入门 - 数据容器 数据容器入门为什么学习数据容器数据容器 数据容器&#xff1a;list(列表)列表的定义嵌套列表的定义列表的下标索引列表的下标(索引)列表的下标(索引) - 反向嵌套列表的下标(索引) 列表的常用操作列表的常用操作(方法)列表的查…

关联规则 关联规则概述

关联规则概述 关联规则 (Association Rules) 反映一个事物与其他事物之间的相互依存性和关联性。如果两个或者多个事物之间存在一定的关联关系&#xff0c;那么&#xff0c;其中一个事物就能够通过其他事物预测到。 关联规则可以看作是一种IF-THEN关系。假设商品A被客户购买&…

【TB作品】基于单片机的机械通风控制系统,实时温度和二氧化碳浓度

硬件&#xff1a; &#xff08;1&#xff09;51系列单片机&#xff0c;拟采用STC89C52RC&#xff1b; &#xff08;2&#xff09;DS18B20温度传感器&#xff1b; &#xff08;3&#xff09;二氧化碳浓度传感器&#xff1a;https://item.taobao.com/item.htm?spma21n57.1.0.0.1…

Unity升级到2022版本后,打开Spine会卡住

1&#xff09;Unity升级到2022版本后&#xff0c;打开Spine会卡住 2&#xff09;iPhone在同时播放多个音效的时候会压低某些音源的音量 3&#xff09;在Y77手机上出现IMGSRV:GetMainShaderConstantBufferBaseAddress: Unsupported 4&#xff09;UE4打包后在部分安卓机型出现“花…

函数栈帧的创建和销毁(编程底层原理)

本篇的内容格外的难写&#xff0c;里面包含了许多的专业术语名和汇编指令等晦涩难懂的东西&#xff0c;既不利于讲解&#xff0c;也不利于读者的理解。但我会尽力去讲述出里面的底层逻辑&#xff0c;帮助大家去理解里面的过程&#xff0c;理解编程的底层原理可以为我们后续更为…

NLP项目实战02:英文文本识别

简介&#xff1a; 欢迎来到本篇文章&#xff01;今天我们将讨论一个新的自然语言处理任务——英文短文识别。具体而言&#xff0c;即通过分析输入的英文文本来判断其是比较消极的还是比较积极的。 展示&#xff1a; 1、项目界面 如下所示是项目启动后用户使用使用界面 2、布…

Redis常用内存淘汰策略?

从淘汰范围来说可以分为不淘汰任何数据、只从设置了到期时间的键中淘汰和从所有键中淘汰三类。而从淘汰算法来分&#xff0c;又主要分为 random&#xff08;随机&#xff09;&#xff0c;LRU&#xff08;最近最少使用&#xff09;&#xff0c;以及 LFU&#xff08;最近最不常使…

终端安全管理软件安装详细攻略来了

随着信息技术的不断发展&#xff0c;终端安全管理软件在企业和组织中发挥着越来越重要的作用。为了确保终端设备的安全和稳定运行&#xff0c;安装终端安全管理软件是必不可少的。以下是一份终端安全管理软件的安装详细攻略&#xff0c;供大家参考。 一、选择合适的软件 首先&…

有哪些好用的运维管理软件?哪个工单管理系统的操作简单一些?

运维管理软件可以帮助企业更有效地管理公司内外的事务&#xff0c;比如现在不少公司就引入了工单管理系统来处理后勤和售后的事务。那么&#xff0c;有哪些好用的运维管理软件&#xff1f;哪个的操作简单一些呢&#xff1f;   随着技术的发展和成熟&#xff0c;现在的工单管理…