java - 在每 100 行 10 000 上使用 flush() 方法会减慢事务

标签 java spring-boot spring-data-jpa entitymanager flush

我有一个示例项目,使用 spring-bootspring-data-jpa 以及 postgres db 和一张表。

我正在尝试将循环中的 INSERT 10 000 条记录放入表中并测量执行时间 - 从 EntityManager< 启用或禁用 flush() 方法 每 100 条记录的类。

预期结果是启用 flush() 方法的执行时间比禁用方法少得多,但实际上我得到了相反的结果。

UserService.java

package sample.data;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    UserRepository userRepository;

    public User save(User user) {
        return userRepository.save(user);
    }
}

UserRepository.java

package sample.data;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> { }

应用程序.java

package sample;

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.Transactional;

import sample.data.User;
import sample.data.UserService;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@SpringBootApplication
@EnableJpaRepositories(considerNestedRepositories = true)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Autowired
    private UserService userService;

    @PersistenceContext
    EntityManager entityManager;

    @Bean
    public CommandLineRunner addUsers() {
        return new CommandLineRunner() {
            @Transactional
            public void run(String... args) throws Exception {
                long incoming = System.currentTimeMillis();
                for (int i = 1; i <= 10000; i++) {
                    userService.save(new User("name_" + i));

                    if (i % 100 == 0) {
                        entityManager.flush();
                        entityManager.clear();
                    }
                }
                entityManager.close();
                System.out.println("Time: " + (System.currentTimeMillis() - incoming));
            }
        };
    }
}

最佳答案

确保在持久性提供程序配置中启用 JDBC 批处理。如果您使用的是 Hibernate,请将其添加到您的 Spring 属性中:

spring.jpa.properties.hibernate.jdbc.batch_size=20   // or some other reasonable value

如果不启用批处理,我猜性能下降是由于每 100 个实体清除一次持久性上下文的开销,但我不确定(你必须测量)。

更新:

实际上,启用或禁用 JDBC 批处理不会影响这样一个事实,即每隔一段时间执行一次 flush() 不会比没有它更快。您使用手动 flush() 控制的不是刷新的方式(通过批处理语句或单一插入),而是您正在控制何时刷新到数据库。

所以你比较的是以下内容:

  1. 每 100 个对象使用 flush():您在刷新时将 100 个实例插入数据库,并且您执行 10000/100 = 100 次
  2. 没有 flush():您只需将上下文中的所有 10000 个对象收集到内存中,并在提交事务时执行 10000 次插入。

另一方面,JDBC 批处理会影响刷新的发生方式,但使用 flush() 与不使用 flush() 时发出的语句数量仍然相同。

在循环中每隔一段时间刷新和清除的好处是避免由于缓存持有太多对象而可能出现的OutOfMemoryError

关于java - 在每 100 行 10 000 上使用 flush() 方法会减慢事务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51728796/

相关文章:

java - spring boot web socket 中的实时通知

spring-boot - Spring Batch 在集群环境中正确重启未完成的作业

spring-boot - 我可以在同一台机器上运行多个Elasticsearch实例进行日志聚合吗?

spring - 如何在spring jpa中以一对多关系插入数据

java - 如何在 Java 中通过套接字发送文件列表

java - 执行hibernate查询时出错

Android SDK工具设置 "Application error"

java - 我的 Directional Symmetry java 算法是否正确?

java - 在 JpaRepository (Spring Data) 中缓存方法

java - @valid 注释未按预期工作