1. 优化去重逻辑
- 内存去重:使用高效的数据结构(如
HashSet
或HashMap
)在内存中去重,避免重复数据进入数据库。 - 分批去重:如果内存不足,可以将数据分批次去重,减少内存压力。
- 并行去重:使用多线程并行处理去重任务。
示例代码:
Set<String> uniqueSet = new HashSet<>();
List<Data> uniqueDataList = dataList.stream()
.filter(data -> uniqueSet.add(data.getUniqueKey())) // 假设getUniqueKey()返回唯一标识
.collect(Collectors.toList());
2. 优化数据库插入
- 批量插入:使用JDBC的
addBatch()
和executeBatch()
方法,减少数据库交互次数。 - 调整批量大小:根据数据库性能调整批量插入的大小(如1000条/批)。
- 禁用索引和约束:在插入前禁用索引和外键约束,插入完成后再启用。
- 使用LOAD DATA INFILE:如果数据库支持(如MySQL),可以将数据写入文件,然后使用
LOAD DATA INFILE
快速导入。
示例代码:
String sql = "INSERT INTO my_table (column1, column2) VALUES (?, ?)";
try (Connection connection = dataSource.getConnection();
PreparedStatement pstmt = connection.prepareStatement(sql)) {
connection.setAutoCommit(false);
for (int i = 0; i < uniqueDataList.size(); i++) {
pstmt.setString(1, uniqueDataList.get(i).getColumn1());
pstmt.setString(2, uniqueDataList.get(i).getColumn2());
pstmt.addBatch();
if (i % batchSize == 0) {
pstmt.executeBatch();
connection.commit();
}
}
pstmt.executeBatch();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
3. 多线程并行处理
- 将数据分片,使用多线程并行处理去重和插入。
- 注意线程安全问题,可以使用线程安全的集合(如
ConcurrentHashMap
)。
示例代码:
ExecutorService executor = Executors.newFixedThreadPool(8); // 根据CPU核心数调整线程数
List<Future<?>> futures = new ArrayList<>();
int chunkSize = uniqueDataList.size() / 8; // 将数据分成8份
for (int i = 0; i < 8; i++) {
int start = i * chunkSize;
int end = (i == 7) ? uniqueDataList.size() : (i + 1) * chunkSize;
List<Data> subList = uniqueDataList.subList(start, end);
futures.add(executor.submit(() -> insertBatch(subList)));
}
for (Future<?> future : futures) {
future.get(); // 等待所有任务完成
}
executor.shutdown();
4. 数据库优化
- 禁用自动提交:在插入数据时禁用自动提交,改为手动提交。
- 调整事务大小:根据数据库性能调整事务的大小,避免事务过大导致锁争用。
- 使用数据库分区:如果数据量非常大,可以考虑对表进行分区,提高查询和插入性能。
5. 使用更高效的工具
- Spring Batch:适合处理大批量数据,支持分片、并行处理、事务管理等。
- Apache Spark:如果数据量极大,可以使用Spark进行分布式去重和插入。
6. 减少数据扫描
- 如果数据源支持(如文件或消息队列),可以在数据读取时直接去重,避免全量扫描。
7. 测试和监控
- 使用性能分析工具(如JProfiler、VisualVM)监控程序性能,找出瓶颈。
- 对数据库进行性能调优,如调整缓冲区大小、连接池配置等。
综合优化示例
public void processLargeData(List<Data> dataList, int batchSize, int threadPoolSize) {
// 去重
Set<String> uniqueSet = new HashSet<>();
List<Data> uniqueDataList = dataList.stream()
.filter(data -> uniqueSet.add(data.getUniqueKey()))
.collect(Collectors.toList());
// 多线程插入
ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
int chunkSize = uniqueDataList.size() / threadPoolSize;
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < threadPoolSize; i++) {
int start = i * chunkSize;
int end = (i == threadPoolSize - 1) ? uniqueDataList.size() : (i + 1) * chunkSize;
List<Data> subList = uniqueDataList.subList(start, end);
futures.add(executor.submit(() -> insertBatch(subList, batchSize)));
}
for (Future<?> future : futures) {
try {
future.get();
} catch (Exception e) {
e.printStackTrace();
}
}
executor.shutdown();
}
private void insertBatch(List<Data> dataList, int batchSize) {
String sql = "INSERT INTO my_table (column1, column2) VALUES (?, ?)";
try (Connection connection = dataSource.getConnection();
PreparedStatement pstmt = connection.prepareStatement(sql)) {
connection.setAutoCommit(false);
for (int i = 0; i < dataList.size(); i++) {
pstmt.setString(1, dataList.get(i).getColumn1());
pstmt.setString(2, dataList.get(i).getColumn2());
pstmt.addBatch();
if (i % batchSize == 0) {
pstmt.executeBatch();
connection.commit();
}
}
pstmt.executeBatch();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
预期效果
通过以上优化,可以将处理时间从100秒显著缩短,具体效果取决于硬件配置、数据库性能和优化策略。如果使用多线程和批量插入,通常可以将时间减少到20-30秒甚至更短。