Dra*_*ved 13 java flush entitymanager spring-data-jpa spring-boot
我有一个示例项目使用spring-bootwith spring-data-jpa和postgres db一个表.
我正在尝试将INSERT10 000条记录循环到表中并测量执行时间 - 为每100条记录启用或禁用类的flush()方法EntityManager.
预期的结果是启用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);
}
}
Run Code Online (Sandbox Code Playgroud)
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> { }
Run Code Online (Sandbox Code Playgroud)
Application.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));
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
确保在持久性提供程序配置中启用JDBC批处理.如果您正在使用Hibernate,请将其添加到Spring属性中:
spring.jpa.properties.hibernate.jdbc.batch_size=20 // or some other reasonable value
Run Code Online (Sandbox Code Playgroud)
在没有启用批处理的情况下,我认为性能回归是由于每100个实体清除持久性上下文的开销,但我不确定(你必须测量).
更新:
实际上,启用JDBC批处理或禁用它不会影响以下事实:flush()每隔一段时间完成一次并不会比没有它更快.什么你使用手动控制flush()是不如何冲洗完成(通过批处理的语句或单一刀片),而是你控制时冲洗到数据库将完成.
所以你要比较的是:
flush()每100个对象:您将100个实例为在冲洗的数据库,你这样做百分之万= 100倍.flush():您只需在内存中的上下文中收集所有10000个对象,并在提交事务时执行10000次插入.另一方面,JDBC批处理会影响刷新的发生方式,但是仍然使用flush()vs without 发出相同数量的语句flush().
在循环中每隔一段时间刷新和清除的好处是避免OutOfMemoryError由于缓存容纳太多对象而导致的可能性.
编写微基准测试非常困难,Aleksey Shipilev 在他的“JMH 与 Caliper:参考线程”帖子中对此进行了很好的说明。您的案例并不完全是一个微观基准,而是:
低于 10,000 次重复不会让 JVM 在默认设置上预热并 JIT 代码。在测量代码性能之前先预热 JVM。
System.nanoTime()不System.currentTimeMillis()用于测量经过的时间。如果您正在测量,ms您的结果将因时钟漂移而产生偏差System.currentTimeMillis()。
您很可能希望在数据库端对此进行测量以查明瓶颈。如果没有瓶颈,就很难理解根本原因是什么,例如您的数据库可能位于大西洋的另一边,并且网络连接成本将掩盖INSERT语句成本。
您的基准测试是否足够隔离?如果数据库由多个用户和连接共享,除了您的基准之外,它的性能也会有所不同。
找到当前设置中的瓶颈,对如何验证它做出假设,更改基准以匹配假设,然后再次测量以确认。这是解决问题的唯一方法。
| 归档时间: |
|
| 查看次数: |
1449 次 |
| 最近记录: |