You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate跨库表数据迁移服务性能慢,求优化方案

数据迁移提速优化方案

你的代码当前一次性加载20万条全量数据到内存,既容易引发内存溢出,又因单次处理数据量过大导致迁移耗时过长,可通过以下方案优化:

1. 分页查询分批处理

不要一次性拉取所有数据,改用分页查询,每次处理固定数量的记录(比如1000条),降低内存占用的同时,避免单次数据库查询压力过大。

修改Repository方法

给TrmInCardClientUKMRepository添加分页查询方法:

Page<TrmInCardClientUKM> findByDeleted(int deleted, Pageable pageable);

修改迁移逻辑

在updatelist方法中循环处理每一页:

public void updatelist() {
    this.log.info("TrmInCardClient data transfer start");
    try {
        int pageSize = 1000;
        int pageNum = 0;
        Page<TrmInCardClientUKM> page;
        do {
            Pageable pageable = PageRequest.of(pageNum, pageSize);
            page = this.trmInCardClientUKMRepository.findByDeleted(0, pageable);
            List<TrmInCardClientDB> trmInCardClientDBList = new ArrayList<>();
            for (TrmInCardClientUKM cardClientUKM : page.getContent()) {
                trmInCardClientDBList.add(new TrmInCardClientDB(
                        cardClientUKM.getCard(), cardClientUKM.getClient(),
                        cardClientUKM.getDeleted(), cardClientUKM.getGlobal_id(), 
                        cardClientUKM.getVersion()
                ));
            }
            this.trmInCardClientDBRepository.saveAll(trmInCardClientDBList);
            pageNum++;
            this.log.info("Completed page " + pageNum + ", processed " + (pageNum * pageSize) + " records");
        } while (page.hasNext());
        this.log.info("TrmInCardClient data transfer end");
    } catch (Exception e) {
        this.log.warning("Error encountered during TrmInCardClient data migration: " + e.getMessage());
    }
}

2. 开启Hibernate批量插入优化

默认情况下Hibernate的saveAll可能还是单条执行插入,需要配置批量提交参数,减少数据库交互次数:

在application.properties中添加:

# 设置批量插入大小
spring.jpa.properties.hibernate.jdbc.batch_size=1000
# 对插入语句排序,提升批量效率
spring.jpa.properties.hibernate.order_inserts=true
# 禁用自动刷新,避免批量操作时频繁flush
spring.jpa.properties.hibernate.flush_mode=COMMIT

3. 迁移增量数据而非全量

当前每次都迁移所有未删除数据,若数据是增量更新的,可只迁移新增/修改的记录,大幅减少处理量:

  • 给表添加last_update_time字段,记录数据最后修改时间
  • 每次迁移时,只查询last_update_time大于上次迁移时间的记录
  • 或者按主键ID分段,记录上次迁移的最大ID,每次只处理ID大于该值的记录

4. 改用JDBC原生批量操作(极端场景下)

如果Hibernate的对象映射开销还是太大,可直接用JDBC的PreparedStatement做批量插入,跳过ORM层的开销:

// 注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;

// 批量插入逻辑
String sql = "INSERT INTO trm_in_card_client_db (card, client, deleted, global_id, version) VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException {
        TrmInCardClientDB entity = trmInCardClientDBList.get(i);
        ps.setString(1, entity.getCard());
        ps.setString(2, entity.getClient());
        ps.setInt(3, entity.getDeleted());
        ps.setString(4, entity.getGlobal_id());
        ps.setInt(5, entity.getVersion());
    }

    @Override
    public int getBatchSize() {
        return trmInCardClientDBList.size();
    }
});

5. 并行处理(可选)

如果服务器CPU和数据库连接资源充足,可将分页后的任务交给线程池并行处理,但需注意控制线程数量,避免数据库连接耗尽:

// 定义线程池
private ExecutorService executor = Executors.newFixedThreadPool(4);

// 在分页循环中提交任务
executor.submit(() -> {
    trmInCardClientDBRepository.saveAll(trmInCardClientDBList);
});

内容的提问来源于stack exchange,提问作者Kether

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:01:09