【问题标题】:Multi threaded insert using ORM?使用ORM的多线程插入?
【发布时间】:2011-09-29 22:50:43
【问题描述】:

我有一个应用程序,其中"persisting to database" 占用了整个应用程序流程的 85%。

我正在考虑使用多个线程来进行插入,因为插入在这里大多是独立的。有没有办法使用任何 JPA 实现来实现多线程插入?或者,从提高性能的角度来看,是否值得进行多线程插入?

注意:一次运行中插入的记录范围为 10K 到 100K。在这里,性能也非常关键。

谢谢。

【问题讨论】:

  • 您能提供更多细节吗?就像正在使用的数据库一样,代码的片段(方法)。这可能会影响您的应用程序,这似乎有点奇怪。
  • 您可能需要的不仅仅是重新设计应用程序来获得您所寻求的效率。如果您的底层数据库表的设计方式不支持多次插入,那么世界上所有的应用程序优化都将无济于事。举个例子,您能否在插入期间避免表上的索引锁?
  • @woliveirajr,@Perception。 This 是用例。使用的数据库是 Oracle 和 eclipselink 作为 ORM 提供者。
  • 从该用例中,您引入的任何多线程都可能会使性能变得更糟,因为锁争用和 IO 带宽问题是受到负面影响的主要因素。

标签: java multithreading performance database-design jpa


【解决方案1】:

数据库上的多线程插入语句不会真正使其执行得更快,因为在大多数数据库中,表需要锁定才能插入。因此,您的线程将只等待它之前的一个完成并在下一个可以插入之前解锁表 - 这实际上并没有使它比单线程更具多线程。如果你在哪里做,它很可能会减慢速度

如果您插入 10k-100k 条记录,您应该考虑使用您使用的数据库原生的批量插入语句批量插入命令。最快的方法是本机批量插入命令,但它要求您不使用 JPA 并直接使用 JDBC 调用来处理您想要对其使用批量命令的插入。

如果您不想使用本机批量命令,我建议使用 Spring 的 JDBCTemplate,它具有模板化批量插入命令。它非常快,我用它在高事务系统上每 30 秒批量插入 10k-20k 个实体,我对性能非常满意。

最后,确保您的数据库表使用正确的索引、键和选项进行了优化。由于您的数据库是瓶颈,因此这应该是您首先希望提高性能的地方之一。

【讨论】:

  • 哪些数据库锁定表以进行插入?这对 Oracle 来说不是这样,我猜大多数其他数据库也不是这样。
【解决方案2】:

在执行此操作时,您是否定期刷新会话?如果没有,您可能会遇到与数据库无关的严重减速。通常,您希望通过在会话中定期调用 flush() 然后 clear() 来“批量”插入(假设您使用的是 JPA 的某些变体)。

【讨论】:

    【解决方案3】:

    这个article 提供了许多使用 JPA 提高批处理写入性能的技巧。我将引用两个应该给你最好的结果的快速参考。

    优化 #6 - 序列 预分配

    我们优化了 应用程序的第一部分,阅读 来自 MySQL 数据库。第二 部分是优化写作 甲骨文。

    写作最大的问题 过程是Id生成是 使用分配大小为 1。这 意味着对于每个插入都会有 成为下一个更新和选择 序列号。这是一个主要的 问题,因为它实际上是翻倍的 数据库访问量。经过 默认 JPA 使用预分配大小 表和序列 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-writing"="JDBC".

    使用的另一个重要方面 批量写作是你必须具备的 相同的 SQL(实际上是 DML)语句 以分组方式执行 单笔交易。一些 JPA 提供者不订购他们的 DML,所以 你最终可以在 订单等两个语句 插入和订单行插入, 使批量写入无效。 好在 EclipseLink 命令和 将其 DML 分组,因此使用批处理 写入减少了数据库访问 从 500 个订单插入和 5000 个 订单行插入到 55(默认 批量大小为 100)。我们可以增加 批量大小使用 "eclipselink.jdbc.batch-writing.size", 所以将批量大小增加到 1000 将数据库访问次数减少到 6 次 页面。

    【讨论】:

      【解决方案4】:

      数据库上的多线程插入语句不会真正让它执行得更快 因为在大多数数据库中,表需要锁定才能插入。所以你的线程会 只是等待它之前的一个完成并在下一个之前解锁桌子 insert - 与单线程相比,它实际上并没有使它成为多线程。如果 你在哪里做,它很可能会减慢它的速度。

      您是说来自同一张表上不同数据库连接的并发插入需要排他锁才能完成吗?我在 Oracle 上对此进行了测试,但我发现情况并非如此。你真的有一个测试用例来支持你在这里写的东西吗?

      无论如何,批量插入当然比一次插入要快得多。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-05-16
        • 1970-01-01
        • 2016-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多