Java 使用JPA提高foreach持久化调用的性能
我们有这样的业务逻辑:Java 使用JPA提高foreach持久化调用的性能,java,multithreading,oracle,hibernate,jpa,Java,Multithreading,Oracle,Hibernate,Jpa,我们有这样的业务逻辑: public class StuffLogic { @Autowired private Util util; // ... public void processStuffs() { // Simply returns a list of 50k objects List<Stuff> list = dao.getManyFilteredStuff(); for (Stuff
public class StuffLogic {
@Autowired
private Util util;
// ...
public void processStuffs() {
// Simply returns a list of 50k objects
List<Stuff> list = dao.getManyFilteredStuff();
for (Stuff act : list) {
act.setStatus(StatusEnum.SomeStatus);
}
util.mergeAll(list);
}
}
public class Util {
// ...
@Transactional
public void mergeAll(List<?> list) {
for (Object o : entities) {
entityManager.merge(o);
}
}
}
公共类填充逻辑{
@自动连线
私有Util Util;
// ...
public void processstuff(){
//只返回50k个对象的列表
List List=dao.getManyFilteredStuff();
用于(材料法案:列表){
act.setStatus(StatusEnum.SomeStatus);
}
util.mergeAll(列表);
}
}
公共类Util{
// ...
@交易的
公共无效合并所有(列表){
用于(对象o:实体){
实体管理器合并(o);
}
}
}
我们有很多实体(约50000个),希望提高性能(根据初步测量,目前我们每秒可以处理1000个实体)
你们当中有人对此有什么提示吗
我们迄今为止所做的尝试:
- 多线程。如果我们使用多线程执行并在不同的线程中持久化处理,似乎不会真正加快执行时间,但会将执行时间降低大约5倍。我们在Oracle上,可能它在更新时使用表锁定而不是行锁定,所以所有线程都必须等待
- 不幸的是,使用单个批量更新不是一个选项,因为我们有许多监听器引入了一些不使用单个update语句的魔术
任何提示都将不胜感激 您可以阅读这篇关于批处理的文章 <>也可以考虑动态生成查询:
UPDATE stuff
SET status = CASE WHEN id = 1 THEN 'status1'
WHEN id = 2 THEN 'status2'
...
您可以阅读这篇关于批处理的文章 <>也可以考虑动态生成查询:
UPDATE stuff
SET status = CASE WHEN id = 1 THEN 'status1'
WHEN id = 2 THEN 'status2'
...
您可以做几件事,当您一次更新所有内容时,一级缓存会不断增长。这可能会增加脏检查所需的时间 因此,在x记录(找到最佳点)之后,在entityManager上进行刷新和清除
public void mergeAll(List<?> list) {
int i = 0;
for (Object o : entities) {
entityManager.merge(o);
i++:
if (i ^ 50 == 0) {
entityManager.flush();
entityManger.clear();
}
}
}
这将减少向oracle发出的查询量,而不是50个单一查询—这将是一个包含50个条目的单一查询
如果您的修改同时导致更新和插入,您可能希望对它们进行排序,以便hibernate可以将它们分组并使用批处理语句
hibernate.order_inserts=true
hibernate.order_updates=true
如果使用版本控制,可能还需要将hibernate.jdbc.batch\u versioned\u data
设置为true
您可以在这些属性上找到一篇很好的文章。您可以做几件事,因为您要一次性更新所有内容,一级缓存会不断增长。这可能会增加脏检查所需的时间 因此,在x记录(找到最佳点)之后,在entityManager上进行刷新和清除
public void mergeAll(List<?> list) {
int i = 0;
for (Object o : entities) {
entityManager.merge(o);
i++:
if (i ^ 50 == 0) {
entityManager.flush();
entityManger.clear();
}
}
}
这将减少向oracle发出的查询量,而不是50个单一查询—这将是一个包含50个条目的单一查询
如果您的修改同时导致更新和插入,您可能希望对它们进行排序,以便hibernate可以将它们分组并使用批处理语句
hibernate.order_inserts=true
hibernate.order_updates=true
如果使用版本控制,可能还需要将hibernate.jdbc.batch\u versioned\u data
设置为true
可以找到关于这些属性的好文章。假设
mergeAll
在单独的事务中执行(否则,如果实体已经在持久性上下文中,则无需调用merge
),那么您将有许多数据库往返(每个实体至少一次)获取要合并的实体
解决方案之一是通过一个查询读取所有这些实体(与处理之前用于读取实体的查询相同,或者通过使用构造从entity where entity.id in(?、、?、…)
)中选择e,然后将其合并,因为合并时实体将处于持久性上下文中
您可能希望将此方法与成批刷新/清除持久性上下文相结合,以实现更好的内存管理,如其他答案所示。假设
mergeAll
在单独的事务中执行(否则,如果实体已在持久性上下文中,则无需调用merge
),然后您将有许多数据库往返(每个实体至少一次)来获取要合并的实体
解决方案之一是通过一个查询读取所有这些实体(与处理之前用于读取实体的查询相同,或者通过使用构造从entity where entity.id in(?、、?、…)
)中选择e,然后将其合并,因为合并时实体将处于持久性上下文中
您可能希望将此方法与成批刷新/清除持久性上下文相结合,以便更好地管理内存,如其他答案所示。我假设对
persist
的调用在foreach
循环中?是的,抱歉,输入错误。基本上,我们在某个地方有一个实用函数,它为每个元素调用persist(),并用@Transactional.Plesae标记一些实际代码,因为目前我对事务没有任何线索,涉及多少实体管理器您使用的是单个循环还是多个循环。信息太少……我假设循环也在一个事务中,并且实际上只有一个事务。拥有5000+1事务不是很快,因为启动/提交很慢。使用批处理而不是单个提交将加快速度,您不应该使用merge
而不是persist
(后者用于新实体)。根据要求,请使用未派生的实际代码,因为您现在拥有的代码无法工作(没有参数,方法名称不匹配).我假设对persist
的调用在foreach
循环中?是的,对不起,输入错误。基本上,我们在某处有一个实用函数,它为每个元素调用persist(),并用@Transactional.Ple标记