jdbcTemplate读数据内存溢出

最近做Hadoop项目的自动化测试,代码要从hadoop mart里读数据出来,写到.csv文件里。有一个query结果集包含10万+条记录,且每条记录包含30+个列,于是jdbcTemplate华丽丽的内存溢出了。其实论数据量,这个真心不算大…
仔细看了看代码,大概知道了原因,结果集保存在List<Map<String, Object>>这种结构中,而Map是比较消耗内存的,10万+个Map,每个Map30+对key-value…内存溢出是可以理解的

public List<Map<String, Object>> querySQL(final String query){
    return jdbcTemplate.queryForList(query);
}

于是改用最原始的statement执行query,设置fetch szie,然后将result set存到List<List<String>>中。每到2000行

### 使用 JdbcTemplate 查询大量数据的最佳实践 当使用 `JdbcTemplate` 进行大量数据查询时,内存管理和性能优化至关重要。以下是针对此场景的一些最佳实践: #### 1. **分页查询** 对于大规模数据集,一次性加载所有记录可能导致内存溢出或显著降低系统性能。因此,推荐采用分页查询的方式逐步获取数据。 分页的核心在于 SQL 中的 LIMIT 和 OFFSET 子句(适用于 MySQL 或 PostgreSQL),或者 TOP 和 ROW_NUMBER() 函数(适用于 Microsoft SQL Server)。以下是一个基于 Spring 的分页实现示例[^1]: ```java public List<YourEntity> fetchDataByPage(int pageNumber, int pageSize) { String sql = "SELECT * FROM your_table ORDER BY id LIMIT ? OFFSET ?"; return jdbcTemplate.query(sql, new Object[]{pageSize, (pageNumber - 1) * pageSize}, new BeanPropertyRowMapper<>(YourEntity.class)); } ``` 上述代码中,`LIMIT` 控制每页返回的数据量,而 `OFFSET` 则指定跳过的记录数。这种方法能够有效减少单次查询的结果集大小,从而缓解内存压力。 #### 2. **流式处理** 如果无法预先知道总记录数量,或者希望进一步优化资源消耗,则可以考虑使用 JDBC 的游标功能配合 `ResultSet` 流式读取数据。Spring 提供了 `setFetchSize()` 方法来控制底层驱动程序的行为[^3]。 下面展示了一个利用流式处理的大规模数据读取方案: ```java public void processLargeDataset(Consumer<YourEntity> consumer) throws SQLException { Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { connection = dataSource.getConnection(); String sql = "SELECT * FROM your_table"; statement = connection.prepareStatement(sql); statement.setFetchSize(Integer.MIN_VALUE); // 启用服务器端光标模式 resultSet = statement.executeQuery(); while (resultSet.next()) { YourEntity entity = mapToEntity(resultSet); consumer.accept(entity); // 对每一行数据单独处理 } } finally { closeQuietly(connection, statement, resultSet); } } private YourEntity mapToEntity(ResultSet rs) throws SQLException { YourEntity entity = new YourEntity(); entity.setId(rs.getInt("id")); entity.setName(rs.getString("name")); // 映射其他字段... return entity; } private void closeQuietly(AutoCloseable... resources) { Arrays.stream(resources).forEach(resource -> { if (resource != null) { try { resource.close(); } catch (Exception ignored) {} } }); } ``` 这种方式避免了一次性将整个结果集载入内存,而是逐条处理记录,非常适合于超大型表的操作。 #### 3. **批量插入/更新** 除了查询外,在某些情况下还需要对大批量数据执行写入操作。此时应优先选用批量 API 来代替逐一调用 insert/update 命令。 例如: ```java String sql = "INSERT INTO your_table (column1, column2) VALUES (?, ?)"; List<Object[]> batchArgs = new ArrayList<>(); for (int i = 0; i < largeDataSet.size(); ++i) { batchArgs.add(new Object[] {largeDataSet.get(i).getColumn1(), largeDataSet.get(i).getColumn2()}); } jdbcTemplate.batchUpdate(sql, batchArgs); ``` 该方法不仅提高了吞吐率,还能大幅削减网络往返次数以及锁等待时间。 --- ### 数据库连接管理的重要性 无论采取何种策略,都需注意合理配置数据库连接池参数以适应高负载环境下的频繁请求。通常建议调整最大活跃连接数、最小闲置连接数及存活检测间隔等属性设置[^2]。 另外,确保每次操作完成后及时释放资源也很重要——这可以通过 try-with-resources 结构自动完成,亦或是显式关闭 Statement 和 ResultSets 实现。 --- ### 总结 综上所述,面对海量数据查询任务时,应当综合运用分页机制、流化技术和批量化手段加以解决;与此同时还要兼顾好基础架构层面的支持条件比如恰当设定 DBCP 参数等等。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值