jdbc查询超大数据集内存溢出

今天使用jdbc的PreparedStatement查询语句如下:

 

 

总是报这个错误:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 at com.mysql.jdbc.Buffer.getBytes(Buffer.java:198)
 at com.mysql.jdbc.Buffer.readLenByteArray(Buffer.java:318)
 at com.mysql.jdbc.MysqlIO.extractNativeEncodedColumn(MysqlIO.java:3567)
 at com.mysql.jdbc.MysqlIO.unpackBinaryResultSetRow(MysqlIO.java:3483)
 at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1391)
 at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:2369)
 at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:451)
 at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:2076)
 at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:1451)
 at com.mysql.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:1314)
 at com.mysql.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:740)
 at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1467)

 

但是从网上找到的doc说,默认情况下不会出这个问题的,因为java回自动的优化不会导致内存溢出的。百思不得其解。

后来无意找到一个介绍,这么来做

就是给url连接字符串添加2个属性即可:useCursorFetch=true&defaultFetchSize=100

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 数据库查询是应用程序与数据交互的关键环节,若一次性从数据库中获取大量数据,易导致内存溢出,即程序申请内存时,因系统资源限制或程序设计不当,无法分配到足够内存空间来完成操作。服务器内存负担过重,系统可能崩溃。以下是解决该问题的多种方法: 分页查询:这是常用方法,通过设定分页参数,每次仅加载一定数量数据,减少一次性加载量。如在SQL中,可用LIMIT或OFFSET关键词实现。 流式查询:允许数据库在结果集未完全生成时就开始返回数据,而非等待所有结果计算完毕。如JavaJDBC,其ResultSet的scrollable特性,可在获取数据同时释放已读取数据,避免一次性加载全部数据。 数据预处理:可将大查询拆分为多个小查询,分别处理,或运用子查询、连接查询等优化策略,减少单次查询数据量。 提升硬件配置:增加服务器内存大小,提升数据库处理大数据能力,但这只是临时方案,无法从根本上解决问题。 数据库优化:对数据库进行索引优化,提高查询效率;使用存储过程或视图封装复杂查询,减少客户端与服务器间通信开销。 引入缓存机制:借助缓存技术(如Redis或Memcached)存储部分数据,减轻数据库压力。对于频繁访问且不常变更的数据,缓存可显著提升性能。 使用数据压缩:在查询过程中,对返回数据进行压缩处理,减少传输数据量。像MySQL等数据库提供压缩协议,可在不影响业务情况下降低内存占用。 实施数据归档:将历史数据定期归档到低成本存储介质上,仅保留最近一段时间数据供查询。 调整JVM堆内存设置:对于Java应用,调整JVM的-Xms和-Xmx参数,合理设定堆内存大小,防止内存溢出。 代码优化:编程时避免集合类无限制增长,及时释放不再使用的对象,减少内存泄露可能。 解决数据库一次查询数据量过大导致的内存溢出
### MyBatis-Plus 查询大量数据导致内存溢出的解决方案 在使用 MyBatis-Plus 进行数据库查询时,如果涉及大量的数据读取,可能会因为一次性加载过多数据到内存中而导致内存溢出Java Heap Space OutOfMemoryError)。以下是几种常见的解决方法: --- #### 一、分页查询 分页查询是一种简单且高效的手段,用于避免一次性加载全部数据。MyBatis-Plus 提供了内置的分页插件,可以通过配置轻松实现分页功能[^1]。 ```java // 初始化分页对象 Page<User> page = new Page<>(currentPage, pageSize); // 执行分页查询 IPage<User> result = userMapper.selectPage(page, null); // 获取分页后的数据列表 List<User> users = result.getRecords(); ``` 通过设置合理的 `pageSize` 和 `currentPage` 参数,可以显著降低单次查询返回的数据量,从而减轻内存负担。 --- #### 二、流式查询 对于超大规模的数据集,分页可能仍然不够理想。此时可以采用流式查询的方式,逐条或逐批处理数据,避免将所有数据一次性加载到内存中[^2]。 ##### 实现步骤: 1. **启用 Cursor 支持** 自 MyBatis 3.4.0 开始引入了 `Cursor` 接口,支持基于游标的流式查询。 2. **配置 JDBC Fetch Size** 确保 MySQL 驱动中的 `setFetchSize(Integer.MIN_VALUE)` 方法已生效,这会通知数据库服务器以流式方式传输数据,而不是一次性将所有结果发送给客户端[^2]。 3. **代码示例**: ```java @Select("SELECT * FROM large_table") public Cursor<LargeTableEntity> selectAll(); // 调用流式查询 try (Cursor<LargeTableEntity> cursor = mapper.selectAll()) { while (cursor.hasNext()) { LargeTableEntity entity = cursor.next(); process(entity); // 对每一行数据单独处理 } } catch (Exception e) { log.error("Stream query failed", e); } ``` 流式查询特别适合于需要全量扫描表但又受限于内存容量的场景。 --- #### 三、调整 JVM 堆内存参数 如果无法改变现有的查询逻辑,或者暂时不适合改造为分页或流式模式,则可以通过增加 JVM 的最大堆空间来缓解内存不足的问题。但这仅是一个权宜之计,并不建议长期依赖此方法[^3]。 修改启动命令,添加以下 JVM 参数以扩大堆内存: ```bash -Xms512m -Xmx4g ``` 其中 `-Xms` 设置初始堆大小,`-Xmx` 设置最大堆大小。单位可选 `m` 或 `g`。 --- #### 四、批量写入与分割操作 如果问题是由于大批量数据插入引起,则应重新设计插入策略。例如,将原本的一次性提交改为小批次循环执行,并及时清理无用的对象引用,以便垃圾回收机制尽早释放资源[^3]。 --- #### 五、优化 SQL 查询语句 除了以上技术层面的优化外,还应对 SQL 查询本身进行审查和优化。例如: - 添加索引以提高查询效率; - 减少不必要的字段选择(即只查询所需的列); - 合理拆分复杂查询为多个简单的子查询。 这些措施都能间接减少内存消耗。 --- ### 结论 综合来看,针对 MyBatis-Plus 查询大量数据导致内存溢出的问题,首选方案是实施分页查询或切换至流式查询模式。这两种方式不仅能够有效控制内存使用量,还能提升系统的稳定性和性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值