当前位置: 首页 > news >正文

面试题:从 MySQL 读取 100w 数据进行处理,应该怎么做?

文章目录

  • 背景
  • 常规查询
  • 流式查询
    • MyBatis 流式查询接口
    • 为什么要用流式查询?
  • 游标查询
    • @Options
    • @ResultType
    • 注意:
    • 原因:
  • 非流式查询和流式查询区别:


背景

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

  • 数据迁移
  • 数据导出
  • 批量处理数据

在实际工作中当指定查询数据过大时,我们一般使用分页查询的方式一页一页的将数据放到内存处理。但有些情况不需要分页的方式查询数据或分很大一页查询数据时,如果一下子将数据全部加载出来到内存中,很可能会发生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 深分页优化情况下,估计你的数据库服务器就噶皮了,或者你能等上几十分钟或几小时,甚至几天时间检索数据。

流式查询

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

如果没有流式查询,我们想要从数据库取 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 by与order by字段不一样之外,其他的场景都非常适合使用流式查询,可以最大限度的降低对客户端内存的消耗。

游标查询

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

当查询百万级的数据的时候,还可以使用游标方式进行数据查询处理,不仅可以节省内存的消耗,而且还不需要一次性取出所有数据,可以进行逐条处理或逐条取出部分批量处理。一次查询指定 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();

相关文章:

面试题:从 MySQL 读取 100w 数据进行处理,应该怎么做?

文章目录 背景常规查询流式查询MyBatis 流式查询接口为什么要用流式查询&#xff1f; 游标查询OptionsResultType注意&#xff1a;原因&#xff1a; 非流式查询和流式查询区别&#xff1a; 背景 大数据量操作的场景大致如下&#xff1a; 数据迁移数据导出批量处理数据 在实际…...

销售转行上位机编程:我的学习与职业经历分享

同学们好&#xff0c;我是杨工&#xff0c;原先是一名销售。 通过在华山编程培训中心学习&#xff0c;成功转行上位机编程&#xff0c;对此我想分享学习和职业经历。 在职业生涯的早期&#xff0c;我并没有考虑将技术融入到我的工作中。然而&#xff0c;在几次创业的失败后&a…...

分库分表之Mycat应用学习一

1 为什么要分库分表 1.1 数据库性能瓶颈的出现 对于应用来说&#xff0c;如果数据库性能出现问题&#xff0c;要么是无法获取连接&#xff0c;是因为在高并发的情况下连接数不够了。要么是操作数据变慢&#xff0c;数据库处理数据的效率除了问题。要么是存储出现问题&#xf…...

Windows下Qt使用MSVC编译出现需要转为unicode的提示

参考 Qt5中文编码问题解决办法_qt5设置编码-CSDN博客 致敬 提示&#xff1a;warning: C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失。 出现此问题&#xff0c;应该是Unix格式下代码的编码格式是UTF-8&#xff0c;注意不…...

【数值分析】乘幂法,matlab实现

乘幂法 一种求实矩阵 A {A} A 的按模最大的特征值&#xff0c;及其对应的特征向量 x i {x_i} xi​ 的方法&#xff0c;只能求一个。特别适合于大型稀疏矩阵。 一个矩阵的特征值和特征向量可以通过矩阵不断乘以一个初始向量得到。 每次乘完之后要规范化&#xff0c;防止上溢或…...

视频监控EasyCVR如何通过设置sei接口,实现在webrtc视频流中添加画框和文字?

安防视频监控系统基于视频综合管理平台EasyCVR视频系统&#xff0c;采用了开放式的网络结构&#xff0c;可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;具备权限管…...

智能三维数据虚拟现实电子沙盘

一、概述 易图讯科技&#xff08;www.3dgis.top&#xff09;以大数据、云计算、虚拟现实、物联网、AI等先进技术为支撑&#xff0c;支持高清卫星影像、DEM高程数据、矢量数据、无人机倾斜摄像、BIM模型、点云、城市白模、等高线、标高点等数据融合和切换&#xff0c;智能三维数…...

【SpringCloud】-GateWay源码解析

GateWay系列 【SpringCloud】-GateWay网关 一、背景介绍 当一个请求来到 Spring Cloud Gateway 之后&#xff0c;会经过一系列的处理流程&#xff0c;其中涉及到路由的匹配、过滤器链的执行等步骤。今天我们来说说请求经过 Gateway 的主要执行流程和原理是什么吧 二、正文 …...

华为无线ac双链路冷备和热备配置案例

所谓的冷备和热备&#xff0c;冷备就是不用vrrp和hsb协议同步ap和用户信息&#xff0c;主的断了等七十五秒后&#xff0c;备的capwap和ap连接上去。 双链路冷备不用vrrp和hsb 双链路热备份只用hsb同步ap和用户信息&#xff0c;不用vrrp&#xff0c;两个ac可以不用在同一个二层…...

VSCode Python开发环境配置

目录 1 插件安装2 Debug和测试配置常见问题 1 插件安装 1.1 基础编译插件&#xff0c;Python、Pylance 1.2 修改语言服务器类型&#xff0c;进入用户配置页面搜索Python: Language Server&#xff0c;选择Pylance&#xff08;一定要修改可以提供很多语法提示&#xff09; 1…...

浅谈【GPU和CPU】

GPU和显卡的区别 GPU&#xff08;Graphics Processing Unit&#xff0c;图形处理器&#xff09;通常指的就是显卡。显卡是一种安装在计算机中的扩展卡&#xff0c;主要用于图形和图像处理任务。 GPU作为显卡的核心组件&#xff0c;负责处理图形渲染、图像处理、视频解码和其他…...

啥是构造器?

当我们new一个对象时就是在引用构造器 构造器又叫做构造函数 构造函数一般分为无参构造函数与有参构造函数 假设我们创建一个pet类&#xff0c;这个类里面就会有一个看不见的自动生成的无参构造函数 如果pet类里没有这个隐形的无参构造&#xff0c;我们new一个对象时就会报错…...

Linux基础知识学习2

tree命令的使用 可以看到dir2目录下的这些文件&#xff0c;要想显示dir2的具体结构&#xff0c;可用tree命令 mv命令 它可以实现两个功能 1.将文件移动到另一个目录中 2.对某一个文件进行重命名 1.将文件移动到另一个目录中 这里将dir1中的2.txt移动到他的子目录dir3中 执行…...

Grafana二进制部署并配置prometheus数据源

1、获取grafna二进制安装包 https://grafana.com/grafana/download?pggraf&plcmtdeploy-box-1 grafana官网下载地址 [rootambari-hadoop1 ~]# cd /opt/module/grafana/ [rootambari-hadoop1 grafana]# pwd /opt/module/grafana2、在安装自己的安装目录执行 wget https:…...

时序预测 | Matlab实现SSA-CNN-BiLSTM麻雀算法优化卷积双向长短期记忆神经网络时间序列预测

时序预测 | Matlab实现SSA-CNN-BiLSTM麻雀算法优化卷积双向长短期记忆神经网络时间序列预测 目录 时序预测 | Matlab实现SSA-CNN-BiLSTM麻雀算法优化卷积双向长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SSA-CNN-BiLSTM麻雀算…...

Java中的单元测试

单元测试 单元测试概述: 单元测试是指在软件开发中对软件的最小可测试单元进行测试和验证的过程。最小可测试单元通常是指函数、方法或者类&#xff0c;单元测试可以保证开发人员的代码正确性&#xff0c;同时也方便后期维护和修改。单元测试的主要目的是检测代码的正确性&am…...

143.【Nginx-02】

Nginx-02 (五)、Nginx负载均衡1.负载均衡概述2.负载均衡的原理及处理流程(1).负载均衡的作用 3.负载均衡常用的处理方式(1).用户手动选择(2).DNS轮询方式(3).四/七层负载均衡(4).Nginx七层负载均衡指令 ⭐(5).Nginx七层负载均衡的实现流程 ⭐ 4.负载均衡状态(1).down (停用)(2)…...

代码随想录刷题 | Day2

今日学习目标 一、基础 链表 接下来说一说链表的定义。 链表节点的定义&#xff0c;很多同学在面试的时候都写不好。 这是因为平时在刷leetcode的时候&#xff0c;链表的节点都默认定义好了&#xff0c;直接用就行了&#xff0c;所以同学们都没有注意到链表的节点是如何定…...

C++ enum class 如何使用

enum class 是 C11 引入的一种新的枚举类型&#xff0c;它是对传统 C 风格的枚举的一种改进。enum class 提供了更强大的类型安全性和作用域限定。以下是关于 enum class 的详细介绍和用法说明&#xff1a; 1. 基本语法 enum class EnumName {Enumerator1,Enumerator2,// ...…...

攻防技术-单包攻击防范:扫描、畸形、特殊(HCIP)

单包攻击类型介绍 一、扫描窥探攻击 1、地址扫描攻击防范 攻击介绍 运用ping程序探测目标地址&#xff0c;确定目标系统是否存活。也可使用TCP/UDP报文对目标系统发起探测&#xff08;如TCP ping&#xff09;。 防御方法 检测进入防火墙的ICMP、TCP和UDP报文&#xff0c;根…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...