【问题标题】:Hibernate Relationship Mapping/Speed up batch insertsHibernate 关系映射/加速批量插入
【发布时间】:2011-02-11 06:09:38
【问题描述】:

我有 5 个 MySQL InnoDB 表:Test,InputInvoice,InputLine,OutputInvoice,OutputLine,每个表都在 Hibernate 中映射和运行。我使用过 StatelessSession/Session 和 JDBC 批量大小。我已经删除了任何生成器类以让 MySQL 处理 id 生成 - 但它的执行速度仍然很慢。 这些表中的每一个都在一个 java 类中表示,并相应地映射到 hibernate 中。目前,当需要写出数据时,我会遍历对象并执行session.save(Object)session.insert(Object)(如果我使用的是StatelessSession)。当我的行数达到最大 jdbc 批处理大小 (50) 时,我还会进行刷新和清除(使用 Session 时)。

  1. 如果我将这些放在一个包含对象的“父”类中并执行 session.save(master) 而不是每个对象,会更快吗?
  2. 如果我将它们放在主/容器类中,我将如何在 hibernate 中映射它以反映这种关系?容器类实际上并不是它自己的表,而是一个全部基于两个索引 run_id (int) 和 line (int) 的关系。
  3. 另一个方向是:如何让 Hibernate 进行多行插入?

【问题讨论】:

  • 我忘了说每个表都有很多列

标签: java mysql performance hibernate orm


【解决方案1】:

ID 生成策略对于 Hibernate 中的批量插入至关重要。特别是,IDENTITY 生成通常起作用(请注意,AUTO 通常也映射到 IDENTITY)。这是因为在批量插入期间,Hibernate 有一个名为“requiresImmediateIdAccess”的标志,表示是否立即需要生成的 ID;如果是,则禁用批处理。

当它说“立即执行身份插入”时,您可以在 DEBUG 级日志中轻松发现这一点 - 这意味着它已跳过批处理,因为它被告知在插入后立即需要生成的 ID。

通常工作的生成策略是 TABLE 和 SEQUENCE,因为 Hibernate 可以预先生成 ID,从而允许批量插入。

确定批量插入是否有效的一种快速方法是激活 DEBUG 级日志,因为 BatchingBatcher 会明确告诉您它正在执行的批量大小(“Executing batch size:” + batchSize)。

此外,以下属性对于实现批量插入很重要。我不敢说它们是必需的,因为我没有足够的 Hibernate 专家这样做——也许这只是我的特殊配置——但根据我的经验,它们仍然是需要的:

hibernate.order_inserts = true
hibernate.order_updates = true

这些属性的文档记录很差,但我相信它们所做的是使 SQL INSERT 和 UPDATE 语句能够正确分组以进行批处理;我认为这可能是您所追求的多行插入。如果我在这方面错了,请不要开枪,我是从记忆中回忆的。

我还将继续假设您设置了以下属性;如果没有,这应该作为一个提醒:

hibernate.jdbc.batch_size = xx

其中 xx 自然是您想要的批量大小。

【讨论】:

  • @JDR:非常感谢您提供所有这些信息 - 我正在将我的 ID 生成更改为原生(这也会禁用批处理吗?),并将添加您的其他想法。我已经有 jdbc.batch_size = 50 但没有 order_inserts/updates。我会让你知道它的效果如何。
  • @JDR:我实施了您的建议,我可以看到它正在将我的插入分组在一起,因此所有表都按顺序插入。它正在插入所有测试,然后是 ALL 等。它向我显示批量大小为 50- 这很好。但是我仍然获得了与之前大致相同的性能——3300 条记录大约需要 5 分钟(请记住,每条记录意味着一个插入到 5 个不同的表中)。
  • @manyxcxi:由于 requiresImmediateIdAccess 变量(使用 H2 和 Hibernate 3.5.1-Final),将 ID 更改为本机禁用批处理。如果批处理器说它正在执行 50 个批处理,那么这几乎就是它正在做的事情,这就是这里的目标。 3300 条记录的 5 分钟似乎有点过分,我可以在大约一秒钟内插入 1000 条记录,甚至还有一些额外的 Hibernate Search 开销(尽管是在嵌入式 H2 数据库上)。您在我可以查看的任何地方都有一些示例输出日志吗?你在运行什么数据库,你使用什么连接池?
  • 我正在使用 MySQL(表是 InnoDB),但我说错了。 5分钟是总运行所需的时间。有很多处理开销,事实上我会冒险猜测多达 90% 是我无法控制的开销(等待来自单独计算引擎的响应)。大体思路是:一次从 CSV 中读取 500 行,变成程序特定对象,发送到计算器,处理响应对象,变成数据库对象,写回批处理。我会尝试清理演出的日志
  • 我忘了说,我偏离了计划,没有使用发电机。我的 java 代码为每个人分配了一个 id,因此 Hibernate 不必处理它。
【解决方案2】:

对我来说,最终的解决方案是使用 voetsjoeba 的回应作为起点。 我的休眠配置使用以下选项:

hibernate.order_inserts = true
hibernate.order_updates = true
  • 我从使用 Session 更改为 StatelessSession

  • 重新订购 处理所有元素的 Java 代码 一次分批一张桌子。所以所有 表 x,然后表 y,等等。

  • 删除了 <generator> 从每个 班级。 Java 现在创建它并 将其分配给对象

  • 创建的逻辑允许我确定是否只是 一个 id 被设置,而不是写 数据库中的“空”行

  • 最后,我打开了dynamic-insert 为我的冬眠课程 像这样的定义:<class name="com.my.class" table="MY_TABLE" dynamic-insert="true">

【讨论】:

    猜你喜欢
    • 2012-02-08
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 2020-01-16
    • 2020-10-11
    • 2020-12-04
    • 2013-05-26
    相关资源
    最近更新 更多