当前位置: 首页 > 知识库问答 >
问题:

Spring Boot JPA Hibernate在SaveAll()上性能低下

林俊晖
2023-03-14

我正在开发一个REST应用程序来加载CSV文件,将它们插入数据库(MYSQL-mysqld Ver 5.7.32),然后以JSON格式查询和显示记录。

问题是,当我尝试使用JPA repository SaveAll()方法持久化记录时,需要花费大量时间(500条记录大约25秒)。

我搜索了解决方案,发现一些配置更改似乎可以解决问题,但没有一个对我有帮助。我改变了日志记录。数量组织。冬眠SQL=DEBUG和spring。jpa。财产。冬眠generate_statistics=true以检查hibernate的工作方式。

无需对应用程序进行任何进一步更改。500条记录的属性结果如下所示:

Id生成类型。自动/ID:14:13:30-14:13:52/插入至:14:13:53/无批次/无URL/23s

17529760 nanoseconds spent acquiring 501 JDBC connections;
11900589 nanoseconds spent releasing 500 JDBC connections;
201149299 nanoseconds spent preparing 1500 JDBC statements;
946268444 nanoseconds spent executing 1500 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
511195616 nanoseconds spent executing 1 flushes (flushing a total of 500 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)

22s用于获取和更新Id。

Hibernate为传递给SaveAll()方法的每个实体执行3个查询:

首先,它获得所需的ID:

  1. 选择next_valid_valhibernate_sequence更新
  2. 更新hibernate_sequence设置next_val=?next_val哪里=?

获取Id后:

  1. 插入到表中

如日志所示,Hibernate为500条记录执行1500条语句。

我尝试按以下方式更改配置:

spring.jpa.properties.hibernate.jdbc.batch_size=30
spring.jpa.properties.hibernate.order_inserts=true

并得到了以下结果:

世代类型。自动/ID:14:23:30-14:23:52/插入至:14:23:53/批量大小:30,订单插入=真/无URL/23s

15672968 nanoseconds spent acquiring 501 JDBC connections;
13474276 nanoseconds spent releasing 500 JDBC connections;
116274083 nanoseconds spent preparing 1001 JDBC statements;
843429450 nanoseconds spent executing 1000 JDBC statements;
222390695 nanoseconds spent executing 17 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
346810255 nanoseconds spent executing 1 flushes (flushing a total of 500 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)

语句减少到1000,执行了17批,但仍然需要22秒才能获得ID,1秒才能执行插入。

我将Id生成策略更改为SEQUENCE,结果是相同的。还有,我加了?reWriteBatchedInserts=true到MYSQL连接URL在一个语句中插入多个实体,但仍然Hibernate每个记录产生1个插入:

2020-11-27 15:33:15.349 DEBUG 7061 --- [nio-8080-exec-1] org.hibernate.SQL                        : insert into enra (account_link_code_n, chanel_nam, contract_type_v, cra_ref_num_v, dms_verified_flag_v, id_type_v, msisdn_nsk, profile_type_v, registration_by, registration_date, report_date, shahkar_id, sim_category_code_v, status_code_v, user_code_n, version, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-11-27 15:33:15.349 DEBUG 7061 --- [nio-8080-exec-1] org.hibernate.SQL                        : insert into enra (account_link_code_n, chanel_nam, contract_type_v, cra_ref_num_v, dms_verified_flag_v, id_type_v, msisdn_nsk, profile_type_v, registration_by, registration_date, report_date, shahkar_id, sim_category_code_v, status_code_v, user_code_n, version, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-11-27 15:33:15.350 DEBUG 7061 --- [nio-8080-exec-1] org.hibernate.SQL                        : insert into enra (account_link_code_n, chanel_nam, contract_type_v, cra_ref_num_v, dms_verified_flag_v, id_type_v, msisdn_nsk, profile_type_v, registration_by, registration_date, report_date, shahkar_id, sim_category_code_v, status_code_v, user_code_n, version, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

我有大约40000条记录的CSV文件,这在我的应用程序中造成了巨大的瓶颈。

我真的不知道我的配置中缺少了什么,但似乎如果我能让hibernate批量获取和更新Id(hibernate_序列),时间消耗会大大减少。

任何帮助都非常感谢。

共有1个答案

匡安宜
2023-03-14

对于一般观众来说,问题是世代类型。因为MySQL不支持序列,所以hibernate使用了带有单独表的变通方法。在每一行之前执行一个读和一个写,因此性能下降。

解决方案:

@Id
@Column(nullable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)

它使用auto_increment功能,MySQL的首选选项。:-)

 类似资料:
  • 问题内容: 我试图理解为什么saveAll具有比Spring Data存储库中的保存更好的性能。我正在使用,可以在这里看到。 为了测试,我创建了10k个实体并将其添加到列表中,这些实体只有一个id和一个随机字符串(对于基准测试,我将该字符串保持不变)。遍历我的列表并调用每个元素,这花费了40秒。调用同一完整列表将在2秒内完成。甚至用30k元素进行调用都花了4秒钟。我确保在执行每个测试之前截断表。即

  • 我正在测试我在activeMQ上工作的代码, 我在里面使用。当我发送大约1000个并发请求来将我的消息排队时,将所有消息排队需要很长时间,有时它只是挂在中间,过了一段时间又开始返回。 我正在使用JDBC消息存储,我知道这可能会影响性能。 这对性能的影响主要是由于虚拟主题吗?,因为在activemq网站上,他们指定了非常高的主题性能(当然是在理想条件下) P、 S:1消息排队和出列大约需要13-15

  • 我需要定期将数据从TMP数据库复制到远程PROD数据库,并在列中进行一些数据修改。当我使用PROD数据库中的postgres_fdw扩展(带有映射外部模式)时,复制一百万条记录的过程将持续6分钟。 但是,当我使用dblink从PROD数据库复制相同的表时(SQL运行在PROD数据库上,而不是TEMP上),该过程持续20秒。 如何优化和缩短从临时数据库复制数据的过程? 我必须在TMP数据库上运行SQ

  • 我正在使用Spring Cloud StreamBridge将消息发布到RabbitMQ交换机。使用本机RabbitMQ完美测试,我可以使用单个生产者轻松获得100kmsgs/s(1个通道)。如果我使用发送StreamBrige(也是1个通道)启动带有时循环的线程,我只获得~20kmsgs/s的类似设置(没有持久性,没有手动打包或确认,相同的Docker容器...)。我使用的是Spring Clo

  • 使用thoses和JPA属性 Ehcache对于相同的查询不是有效的, 问题与QueryCache类的函数namedParameters.hashCode()有关,它为同一个查询生成不同的HashCode! 这与类有关 它为同一个数组对象[01,1]生成一个不同的(新的)hachCode! 此hashCode方法对于数组应该是递归的

  • 问题内容: 使用这些JPA属性 Ehcache对于同一查询效率不高, 问题与QueryCache类的namedParameters.hashCode()函数有关,它为同一查询生成了不同的HashCode! 与班级有关 它将为同一Array对象[01,1]生成一个不同的(新)hachCode! 对于数组,此hashCode方法应该是递归的 问题答案: 递归版本完全正常 类org.hibernate.