我有一个示例项目,使用 spring-boot
和 spring-data-jpa
以及 postgres db
和一张表。
我正在尝试将循环中的 INSERT
10 000 条记录放入表中并测量执行时间 - 从 EntityManager< 启用或禁用
每 100 条记录的类。flush()
方法
预期结果是启用 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()
控制的不是刷新的方式(通过批处理语句或单一插入),而是您正在控制何时刷新到数据库。
所以你比较的是以下内容:
- 每 100 个对象使用
flush()
:您在刷新时将 100 个实例插入数据库,并且您执行 10000/100 = 100 次。 - 没有
flush()
:您只需将上下文中的所有 10000 个对象收集到内存中,并在提交事务时执行 10000 次插入。
另一方面,JDBC 批处理会影响刷新的发生方式,但使用 flush()
与不使用 flush()
时发出的语句数量仍然相同。
在循环中每隔一段时间刷新和清除的好处是避免由于缓存持有太多对象而可能出现的OutOfMemoryError
。
关于java - 在每 100 行 10 000 上使用 flush() 方法会减慢事务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51728796/