
本文旨在解决从数据库读取大量数据时遇到的JVM堆内存溢出问题。通过分析问题代码,我们将提供一种使用分页查询(LIMIT和OFFSET)的解决方案,以及相应的代码示例和注意事项,帮助开发者避免一次性加载大量数据,从而有效控制内存使用,提升系统稳定性。
在微服务架构中,处理大量数据迁移或归档任务时,一次性加载百万级甚至更多的数据记录很容易导致JVM堆内存溢出(Resource Exhaustion Event: the JVM was unable to allocate memory from the heap)。直接增加应用内存虽然可以暂时解决问题,但并非长久之计。更优的方案是采用分页查询,分批次处理数据,从而降低单次操作的内存消耗。
核心思想是将原本一次性读取所有数据的操作,分解为多次小批量读取。在SQL查询中,LIMIT 用于限制返回的记录数量,OFFSET 用于指定从哪条记录开始返回。
以下是一个简化的SQL示例:
SELECT * FROM your_table WHERE your_condition ORDER BY your_order_column LIMIT batch_size OFFSET offset_value;
结合上述SQL,我们可以将原有的archiveTableRecords方法进行改造,实现分页读取:
@Value("${batch-size}")
private int batchSize;
public void archiveTableRecords(JdbcTemplate sourceDbTemplate, JdbcTemplate targetDbTemplate,
ArchiveConfigDTO archiveObj) {
try {
String sourceTable = archiveObj.getSourceTable();
String archive_months = archiveObj.getArchiveCriteriaMonths();
String compareDate1 = getCSTDateNew(archive_months);
String primaryKeyColumn = archiveObj.getPrimaryKeyColumn();
logger.info("Archive criteria date: {}", compareDate1);
int offset = 0;
List<Map<String, Object>> sourceRecords;
do {
// 构建分页查询SQL
String sql = buildSQLQueryToFetchSourceRecords(sourceTable, compareDate1, batchSize, offset, primaryKeyColumn);
sourceRecords = sourceDbTemplate.queryForList(sql);
int sourceRecordsSize = sourceRecords.size();
logger.info("Fetched {} {} record(s) from offset {}", sourceRecordsSize, sourceTable, offset);
if (sourceRecordsSize > 0) {
List<Object> primaryKeyValueList = new ArrayList<>();
int recordsInserted = copySourceRecords(targetDbTemplate, archiveObj.getTargetTable(),
primaryKeyColumn, sourceRecords, primaryKeyValueList);
if (recordsInserted > 0) {
deleteSourceRecords(sourceDbTemplate, sourceTable, primaryKeyColumn,
primaryKeyValueList);
}
offset += sourceRecordsSize; // 更新偏移量
}
} while (!sourceRecords.isEmpty()); // 当没有更多数据时结束循环
} catch (Exception e) {
logger.error("Exception in archiveTableRecords: {} {}", e.getMessage(), e);
}
}
public String buildSQLQueryToFetchSourceRecords(String sourceTable, String compareDate, int batchSize, int offset, String primaryKeyColumn) {
StringBuilder sb = new StringBuilder("SELECT * FROM " + sourceTable + " where update_dts <= ? ORDER BY " + primaryKeyColumn + " LIMIT " + batchSize + " OFFSET " + offset);
return sb.toString();
}代码解释:
通过使用分页查询,我们可以有效地避免一次性加载大量数据导致的JVM堆内存溢出问题。这种方法不仅降低了内存消耗,还提高了系统的稳定性和可扩展性。在实际应用中,请根据数据量和系统资源合理调整batchSize,并结合事务控制和异常处理,确保数据迁移或归档的正确性和完整性。
以上就是解决JVM堆内存溢出:大数据量读取优化方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号