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

Spring JPA结合PostgreSQL写入性能极低,求优化方案

问题描述

当前运行基于Spring Boot 3、Spring JPA和PostgreSQL 17的应用,需向数据库写入15000至500000条Project数据,但目前写入速度仅*<100条/秒*,数千条数据需耗时数分钟,远达不到预期。

PC配置:32GB DDR5内存、AMD Ryzen 9 7900X处理器,PostgreSQL运行在WSL环境中。

相关代码

Project实体类

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Project {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Basic(optional = false)
  Long id;

  String groupId;
  String artifactId;
  String latestVersion;
  String latestRelease;
  Long lastUpdated;

  @ElementCollection(fetch = FetchType.EAGER)
  List<Version> versions;

  public Project(String groupId, String artifactId, List<Version> versions, String latestVersion, String latestRelease, Long lastUpdated) {
    ...
  }

}

Version可嵌入类

@Embeddable
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Version {

  String version;
  String repository;

}

数据保存代码

for(Project p : projects.values()) {
      if(savingDone % 100 == 0) {
        System.out.printf("\rSaving project " + savingDone + "/" + total);
      }

      projectRepository.save(p);
      savingDone++;
}

application.properties配置

spring.datasource.hikari.data-source-properties.useUnicode=true
spring.datasource.hikari.data-source-properties.characterEncoding=UTF-8

spring.datasource.url=jdbc:postgresql://localhost:5432/<database>
spring.datasource.username=...
spring.datasource.password=...
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

注:使用Apache Cassandra或H2数据库时,写入速度远快于当前情况,求可优化的点。


优化方案

1. 改用批量插入替代单条保存

当前循环单条调用save()会触发大量独立SQL请求和事务提交,开销极大。

  • 使用Spring Data JPA的saveAll(Collection<S> entities)批量保存数据
  • 配合Hibernate批量插入配置,在application.properties中添加:
    spring.jpa.properties.hibernate.jdbc.batch_size=500
    spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
    spring.datasource.hikari.data-source-properties.rewriteBatchedStatements=true
    
  • 批量大小建议设为500-1000(根据内存调整),避免单次批量过大导致内存溢出

2. 调整@ElementCollection的Fetch策略

当前FetchType.EAGER会在插入Project时立即关联插入所有Version,且每次操作都强制加载关联数据,增加额外开销。

  • 改为FetchType.LAZY:仅在需要访问versions列表时才加载,插入时避免不必要的查询
  • 若必须保留EAGER,确保配合上述批量配置,让Hibernate批量处理关联的Version数据

3. 优化Hikari连接池配置

默认配置无法满足高并发写入需求,调整关键参数:

spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
  • 最大连接数建议设为CPU核心数的2倍(你的配置对应20)

4. 优化PostgreSQL(WSL环境)配置

WSL环境下IO性能易成为瓶颈,修改postgresql.conf:

  • shared_buffers = 8GB(设为物理内存的1/4)
  • work_mem = 64MB
  • maintenance_work_mem = 1GB
  • wal_buffers = 16MB
  • checkpoint_completion_target = 0.9
  • 业务允许的话,临时设置synchronous_commit = off(牺牲部分持久性换取写入速度)
  • 确保WSL使用ext4文件系统,避免NTFS的IO损耗

5. 手动控制事务与缓存

当前单条保存会创建大量隐式事务,改为手动批量控制:

@Transactional
public void batchSaveProjects(Collection<Project> projects) {
    int batchSize = 500;
    int count = 0;
    for (Project p : projects) {
        projectRepository.save(p);
        count++;
        if (count % batchSize == 0) {
            entityManager.flush();
            entityManager.clear(); // 清除一级缓存,避免内存膨胀
        }
    }
    entityManager.flush();
    entityManager.clear();
}

6. 禁用DDL自动更新

批量插入前确保表结构已创建完成,将spring.jpa.hibernate.ddl-auto设为none,避免Hibernate每次启动检查更新表结构的额外开销。

7. 优化ID生成策略

当前GenerationType.AUTO在PostgreSQL中默认用序列生成ID,频繁请求序列会影响性能:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "project_seq")
@SequenceGenerator(name = "project_seq", sequenceName = "project_sequence", allocationSize = 500)
Long id;
  • allocationSize设为批量大小,减少Hibernate向数据库请求序列值的次数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:55:58