我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Mybatis流式游标查询-大数据DB查询OOM查询问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Mybatis流式游标查询-大数据DB查询OOM查询问题

问题场景

Mysql数据处理类型分以下三种

com.mysql.cj.protocol.a.result.ResultsetRowsStatic:普通查询,将结果集一次性全部拉取到内存

com.mysql.cj.protocol.a.result.ResultsetRowsCursor:游标查询,将结果集分批拉取到内存,按照fetchSize大小拉取,会占用当前连接直到连接关闭。在mysql那边会建立一个临时表写入磁盘(查询结束后由mysql回收处理),会导致mysql server磁盘io飙升。

com.mysql.cj.protocol.a.result.ResultsetRowsStreaming:流式查询,将结果集一条一条的拉取进内存,比较依赖网络,可能会造成网络阻塞。占用当前mysql连接。

所以在普通查询大数据量时如果JVM内存不够用会出现OOM异常。如下测试方案

数据量20w,一条数据大概2K。

虚拟机参数 -Xmx256m -Xms256m

(1)普通查询,大概接近200多M就GC释放

(2)流式查询,不会出现内存溢出

(3)游标查询,不会出现内存溢出

执行原理—分析

参考:https://machen.blog.csdn.net/article/details/112169908

JDBC 与 MySQL 服务端的交互是通过 Socket 完成的,完整请求链路

JDBC 客户端 -> 客户端 Socket -> MySQL -> 检索数据返回 ->MySQL 内核Socket 缓冲区-> 网络-> 客户端Socket Buffer -> JDBC 客户端

普通查询的方式在查询大数据量时,所在 JVM 可能会凉凉,原因如下:

MySQL Server 会将检索出的SQL 结果集通过输出流写入到内核对应的 Socket Buffer

内核缓冲区通过 JDBC 发起的TCP 链路进行回传数据,此时数据会先进入 JDBC 客户端所在内核缓冲区

JDBC 发起 SQL 操作后,程序会被阻塞在输入流的 read 操作上,当缓冲区有数据时,程序会被唤醒进而将缓冲区数据读取到 JVM 内存中

MySQL Server 会不断发送数据,JDBC 不断读取缓冲区数据到 Java 内存中,虽然此时数据已到 JDBC 所在程序本地,但是 JDBC 还没有对 execute 方法调用处进行响应,因为需要等到对应数据读取完毕才会返回

弊端就显而易见了,如果查询数据量过大,会不断经历 GC,然后就是内存溢出

普通查询等待时间与游标查询等待时间原理上是不一致的,前者是一致在读取网络缓冲区的数据,没有响应到业务层面;后者是 MySQL 在准备临时数据空间,没有响应到 JDBC

游标查询消费完fetchSize 行数据,就需要发起请求到服务端请求

流式查询

当客户端与MySQL Server 端建立起连接并且交互查询时,MySQLServer 会通过输出流将SQL 结果集返回输出,也就是 向本地的内核对应的 SocketBuffer 中写入数据,然后将内核中的数据通过TCP 链路回传数据到JDBC 对应的服务器内核缓冲区

JDBC 通过输入流 read 方法去读取内核缓冲区数据,因为开启了流式读取,每次业务程序接收到的数据只有一条

MySQL 服务端会向 JDBC 代表的客户端内核源源不断的输送数据,直到客户端请求 Socket 缓冲区满,这时的 MySQL 服务端会阻塞

对于JDBC 客户端而言,数据每次读取都是从本机器的内核缓冲区,所以性能会更快一些,一般情况不必担心本机内核无数据消费(除非MySQL 服务端传递来的数据,在客户端不做任何业务逻辑,拿到数据直接放弃,会发生客户端消费比服务端超前的情况)

代码实现—使用

依赖

   org.mybatis   mybatis   3.4.1   org.mybatis   mybatis-spring   1.3.0

流式查询

Mapper接口---返回值为void,依靠ResultHandler进行结果处理

void queryAllTest(ResultHandler resultHandler);

xml定义-----fetchSize为Integer.MIN_VALUE

    select * from eppc_db.t_trade_order

以上也可以用注解实现,如下

// @ResultType(TradeOrderDO.class)// @Select("select * from eppc_db.t_trade_order order by Fpkid desc") //@Options(resultSetType = ResultSetType.FORWARD_ONLY,fetchSize = Integer.MIN_VALUE) void queryAllTest(ResultHandler resultHandler);

Service层

@Overridepublic List queryList() {    List tradeOrderDOList = new ArrayList<>();    List cardIds = new ArrayList<>();    AtomicInteger i = new AtomicInteger(0);    tradeinfoDAO.queryAllTest(resultHandler ->{        TradeOrderDO resultObject = resultHandler.getResultObject();        if (i.get() % 100000 == 0){//此处做业务处理            System.out.println(resultObject.getPkid());// tradeOrderDOList.add(resultHandler.getResultObject());        }        i.getAndIncrement();    });    return tradeOrderDOList;}

游标查询 2种方式

方式1

Mapper接口-----这种是在mapper层直接定义返回游标封装信息

//@Options(resultSetType = ResultSetType.FORWARD_ONLY,fetchSize = Integer.MIN_VALUE) //@Select("select * from eppc_db.t_trade_order")// @ResultType(TradeOrderDO.class) Cursor getAllRecord();

方式2---需要在service层使用sqlSession调用

//@Options(resultSetType = ResultSetType.FORWARD_ONLY,fetchSize = Integer.MIN_VALUE) //@Select("select * from eppc_db.t_trade_order")// @ResultType(TradeOrderDO.class)List getAllRecords();

Service层---需注意加上事务注解表示该service并不是在mapper结束时结束事务,而是等整个service结束才结束事务,不然会出现只能读取到第一段游标的结果集。

@Override@Transactional(readOnly = true)public List getAllRecord() {    List tradeOrderDOList = new ArrayList<>();    Cursor cursor = null;    SqlSession sqlSession = null;    try {        cursor = tradeinfoDAO.getAllRecord();//方式1调用    sqlSession = sqlSessionFactory.openSession();cursor = sqlSession.selectCursor(TradeinfoDAO.class.getName() + ".getAllRecords");//方式2调用        int currentIndex = 0;        Iterator iterator = cursor.iterator();        while (iterator.hasNext()){            System.out.println(iterator.next()+""+currentIndex);                        currentIndex ++;        }    } catch (Exception e) {        e.printStackTrace();    } finally {        if (null != cursor) {            try {                cursor.close();            } catch (Exception e) {                log.error(e.getMessage(), e);            }        }if (null != sqlSession) {          try {         sqlSession.close();     } catch (Exception e) {          log.error(e.getMessage(), e);        }        return tradeOrderDOList;    }}

使用总结

当遇到大数据量查询时确实可以使用mybatis的游标或者游式查询,Mysql底层也支持。但这只是减缓了数据库服务器的读与传输的压力。到业务层面还是需要根据具体业务场景去分批处理,比如一条查300w数据,游式查询能支持,但也不能一起性放入java的list中,内存不够还是会溢出。这时可能就需要写一些条件一次处理多少数据,所以本质来说就是数据不一次性存储,但总有地方要把这些数据存着。不给JVM内存,那就会牺牲网络或者服务器的其它属性。

来源地址:https://blog.csdn.net/weixin_42740540/article/details/129047465

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Mybatis流式游标查询-大数据DB查询OOM查询问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

MySQL中的流式查询及游标查询方式

目录一、业务场景二、罗列一下三种处理方式2.1 常规查询2.2 流式查询2.3 游标查询三、RowData3.1 RowDataStatic3.2 RowDataDynamic3.3 RowDataCursor四、JDBC 通信原理4.1
2022-08-17

MySQL中使用流式查询避免数据OOM

一、前言 程序访问MySQL数据库时,当查询出来的数据量特别大时,数据库驱动把加载到的数据全部加载到内存里,就有可能会导致内存溢出(OOM)。 其实在MySQL数据库中提供了流式查询,允许把符合条件的数据分批一部分一部分地加载到内存中,可以
2022-05-15

Mybatis游标查询大量数据的方法是什么

这篇文章主要讲解了“Mybatis游标查询大量数据的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Mybatis游标查询大量数据的方法是什么”吧!Mybatis游标查询大量数据对大
2023-06-29

Mybatis集成MySQL使用游标查询处理大批量数据方式

MyBatis集成MySQL游标查询,通过将结果集保留在数据库中,减少网络开销,延迟结果集处理和处理大数据集。优点包括减少网络消耗、分批处理和轻量化资源消耗。缺点包括潜在锁定、资源占用和复杂性。使用步骤涉及建立、打开、获取和关闭游标。MyBatis集成需要创建游标、配置连接和使用SqlSession创建游标。示例代码演示了如何使用游标查询。注意事项强调及时关闭游标、性能优化和使用批处理模式。
Mybatis集成MySQL使用游标查询处理大批量数据方式
2024-04-02

mybatis-plus查询无数据问题及解决

这篇文章主要介绍了mybatis-plus查询无数据问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-12-08

MyBatis传入List集合查询数据问题

这篇文章主要介绍了MyBatis传入List集合查询数据问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-02-07

MyBatis-Plus查询不到数据但使用SQL可以查询到数据的问题排查解决

目录前言js一、问题描述示例代码二、排查步骤1. 检查数据源配置2. 检查实体类与数据库表结构3. 检查 Mapper 接口4. 检查 MyBATis-Plus 配置5. 排查查询条件6. 检查日志输出7. 检查数据库连接问题8. 检查全局
MyBatis-Plus查询不到数据但使用SQL可以查询到数据的问题排查解决
2024-09-20

MyBatisPlus 大数据量查询慢的问题解决

本文主要介绍了MyBatis Plus 解决大数据量查询慢问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-05

如何解决Mybatis查询时数据丢失的问题

这篇文章主要为大家展示了“如何解决Mybatis查询时数据丢失的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决Mybatis查询时数据丢失的问题”这篇文章吧。Mybatis查询时数据
2023-06-28

MyBatis传入List集合查询数据问题怎么解决

这篇文章主要介绍“MyBatis传入List集合查询数据问题怎么解决”,在日常操作中,相信很多人在MyBatis传入List集合查询数据问题怎么解决问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MyBatis
2023-07-05

编程热搜

目录