我有一个应用程序,其中"persisting to database"
消耗了整个应用程序流程的85%时间。
我正在考虑使用多个线程来执行插入,因为插入在这里大多是独立的。有没有办法使用任何JPA实现实现多线程插入?或者从提高性能的角度来看,是否值得进行mutli线程插入?
注意:插入在一次运行中的记录范围为10K到100K。此外,表现非常关键。
感谢。
答案 0 :(得分:4)
数据库上的多线程插入语句实际上不会使其执行得更快,因为在大多数数据库中,表需要锁定插入。所以你的线程将只是等待它之前的那个完成并在下一个可以插入之前解锁表 - 这实际上不会使它比单个线程更多线程。如果你去哪里,它会最有可能减慢它。
如果您插入10k-100k记录,则应考虑使用您使用的数据库本机的批量插入语句或批量插入命令。最快的方法是本机批量插入命令,但它要求您不要使用JPA并直接使用JDBC调用来处理要使用批量命令的插入。
如果您不想使用本机批量命令,我建议使用具有模板化批量插入命令的Spring JDBCTemplate。它非常快,我用它在高交易系统上每30秒批量插入10k-20k实体,我对性能非常满意。
最后,确保使用正确的索引,键和选项优化数据库表。由于您的数据库是瓶颈,因此这应该是您希望提高性能的第一个地方。
答案 1 :(得分:2)
数据库上的多线程插入语句不会真正使它更快地执行 因为在大多数数据库中,表需要锁定插入。所以你的线程会 只是等待它前面的一个完成并在下一个罐之前解锁表 insert - 它实际上不会使它比单线程更多线程。如果 你去哪里做,很可能会减慢速度。
您是说同一个表中来自不同数据库连接的并发插入需要完成独占锁定吗?我在Oracle上测试了这个,我没有发现这种情况。你真的有一个测试用例来备份你在这里写的东西吗?
无论如何,批量插入当然比一次插入要快很多。
答案 2 :(得分:1)
这样做时,您是否定期刷新会话?如果没有,你可以点击与数据库无关的令人讨厌的减速。通常,您希望通过在会话中定期调用flush()
然后clear()
来“批量”插入(假设您使用的是某些JPA变体)。
答案 3 :(得分:1)
这个article提供了许多提高JPA批处理写入性能的技巧。我会引用两个应该给你最好结果的快速参考。
优化#6 - 序列 预分配
我们优化了 第一部分应用,阅读 来自MySQL数据库。第二 部分是优化写作 甲骨文。
写作的最大问题 进程是Id生成的 使用分配大小为1.这 意味着每次插入都会 是下一个更新和选择 序列号。这是一个专业 问题,因为它实际上是倍增的 数据库访问量。通过 默认JPA使用预分配大小 对于TABLE和SEQUENCE Id,为50 生成,1代表IDENTITY Id 一代(一个很好的理由) 永远不要使用IDENTITY Id代)。但 经常申请 不必要的偏执洞 他们的Id值和设置 预分配值为1.通过更改 预分配大小从1到500, 我们减少了大约1000个数据库访问 每页。
优化#8 - 批量写作
许多 数据库提供了一种优化 允许批量写入操作 作为单个数据库执行 访问。有参数化和 动态批量写作。对于 参数化批量写入单个 参数化的SQL语句即可 用一批参数执行 vales而不是一组 参数值。这是非常理想的 因为SQL只需要执行 一次,所有数据都可以 最佳地传递到数据库。
动态批量写入需要动态 (非参数化)批处理的SQL 成一个大的声明并发送 一次到数据库。该 然后数据库需要处理这个 巨大的字符串并执行每个 声明。这需要数据库 做了很多解析的工作 声明,所以并非总是最优的。它 确实减少了数据库访问,所以如果 数据库很远或很差 与应用程序连接,这个 可以带来改善。
一般参数化批量写作 更优化,在Oracle上 提供巨大的利益,在哪里 动态没有。 JDBC定义了API 用于批量写入,但不是所有JDBC 司机支持它,一些支持 API然后执行语句 一个接一个,所以测试很重要 您的数据库支持 使用前优化。在 EclipseLink批处理写入已启用 使用持久性单元属性 “eclipselink.jdbc.batch写入”= “JDBC”。
使用的另一个重要方面 批量写作是你必须拥有的 相同的SQL(DML实际)语句 以分组方式执行 单笔交易。一些JPA 提供商不订购他们的DML,所以 你最终可以打乒乓球 两个陈述,如订单 插入和订单行插入, 使批量写入无效。 幸运的是EclipseLink命令和 将其DML分组,以便批量使用 写入减少了数据库访问 从500个订单插入和5000 订单行插入到55(默认值 批量大小为100)。我们可以增加 批量大小使用 “eclipselink.jdbc.batch-writing.size” 所以将批量大小增加到1000 将数据库访问量减少到6个 页。