处理200万条数据去重并入库,耗时100秒,如何优化进一步提升性能?

1. 优化去重逻辑

  • 内存去重:使用高效的数据结构(如HashSetHashMap)在内存中去重,避免重复数据进入数据库。
  • 分批去重:如果内存不足,可以将数据分批次去重,减少内存压力。
  • 并行去重:使用多线程并行处理去重任务。

示例代码:

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秒甚至更短。

优化大批量数据入库性能的关键在于减少I/O操作次数和利用数据库的内置功能。以下是一些策略: 1. **批量插入**:尽量一次性提交多个记录到数据库,而不是逐条插入。例如,在Entity Framework中,你可以使用`AddRange()`方法添加多个实体到单位工作区(UnitOfWork),最后一起SaveChanges()。 ```csharp List<MyEntity> entities = ...; // 批量数据 dbContext.MyEntities.AddRange(entities); dbContext.SaveChanges(); ``` 2. **延迟加载**:避免在插入时立即加载关联的大量数据,这可能会阻塞数据库事务。等到真正需要时再查询。 3. **使用事务**:对于大量的数据操作,应该使用数据库事务保证数据的一致性。但是注意,如果事务过大,可能会导致锁争用,影响性能,因此需要合理地控制事务大小。 4. **数据库连接池**:保持数据库连接的有效利用,使用连接池管理连接,避免频繁建立和关闭连接。 5. **表分区**:对于大型表,使用分区可以提高插入速度,允许对特定部分进行更快的访问。 6. **索引优化**:确保插入字段有合适的索引,特别是在频繁作为主键或联合唯一键的字段上。 7. **存储过程**:如果有条件,使用存储过程进行插入操作,因为它们通常比直接SQL查询更有效率。 8. **硬件升级**:增加内存、优化磁盘I/O性能或使用更快的网络连接也可能有助于提升性能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值