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
相关产品推荐
相关产品推荐

