【问题标题】:How to optimize one big insert with hibernate如何使用休眠优化一个大插入
【发布时间】:2019-07-05 14:45:13
【问题描述】:

对于我的网站,我正在创建一个图书数据库。我有一个目录,有一个根节点,每个节点都有子节点,每个子节点都有文档,每个文档都有版本,每个版本由几个段落组成。 为了尽可能快地创建这个数据库,我首先在内存中创建整个树模型,然后调用 session.save(rootNode) 这个单一的保存将填充我的整个数据库(最后,当我在它权重为 1Go 的数据库上执行 mysqldump 时) 保存时间很长(超过一个小时),并且由于数据库随着新书和现有书籍的新版本而增长,所以它的速度越来越快。我想优化这个存档。

我已尝试增加 batch_size。但它没有任何改变,因为它是一个独特的保存。当我 mysqldump 一个脚本,并将其插入 mysql 时,运行时间为 2 分钟或更短。 当我在 ubuntu 机器上执行“htop”时,我可以看到 mysql 只使用 2% 或 3% 的 CPU。也就是说谁慢就是hibernate。

如果有人能给我一些我可以尝试的技术或可能的线索,那就太好了……我已经知道一些原因,为什么这需要时间。如果有人想和我讨论,谢谢他的帮助。

这是我的一些问题(我认为):例如,我的大多数实体都有自己分配的 ID。因此,hibernate 每次在保存之前检查该行是否存在。我不需要这个,因为当我从头开始创建数据库时,我正在执行的批处理只执行一次。最好的办法是告诉 hibernate 忽略 primaryKey 规则(就像 mysqldump 一样)并在创建数据库后重新启用密钥检查。这只是一个批量,用于初始化我的数据库。

第二个问题又是关于外键的。 Hibernate 插入具有空值的行,然后进行更新以使外键起作用。

关于使用另一种技术:我想让这个批处理与 hibernate 一起工作,因为之后,我所有的网站都可以很好地与 hibernate 一起工作,如果是 hibernate 创建数据库,我确定命名规则,并且每个外键将被很好地创建。

最后,它是一个只读数据库。 (我有一个用户数据库,它正在使用 innodb,我在其中进行更新,并在我的网站运行时插入,但文档数据库是只读的并且是 mYisam)

这是我正在做的一个例子

TreeNode rootNode = new TreeNode();
recursiveLoadSubNodes(rootNode); // This method creates my big tree, in memory only.

hibernateSession.beginTrasaction();
hibernateSession.save(rootNode); // during more than an hour, it saves 1Go of datas : hundreads of sub treeNodes, thousands of documents, tens of thousands paragraphs.
hibernateSession.getTransaction().commit();

【问题讨论】:

  • “我已经知道一些原因,为什么需要时间” - 请注意,将这些以及您对这些的推理纳入您的问题可能会有所帮助。这表明你付出了努力,更容易理解你的情况(问题、知识等),从而更容易提供建议。
  • 对不起。在解释所有内容之前,我只是想先知道我是否在正确的论坛上。我在第一篇文章中添加了一些细节。
  • 请托马斯,我能问你点什么吗?完成我的问题的最佳方法是什么(就像你说我可以从一开始就给出详细信息)?我应该回答自己的帖子,还是应该完成并编辑第一篇帖子?如果我编辑问题,答案可能看起来离题。 (对不起,我是第一次寻求帮助)
  • “我在第一篇文章中添加了一些细节。” - 请注意,最好通过edit 为您的问题添加详细信息。否则它可能会变得混乱。至于细节本身:这取决于(哎呀,非常有帮助;))。包括模型的一些相关部分(和映射)、你的保存过程、你观察到的细节,例如日志条目 - 如果它们太长,那么您应该尝试删除不必要的部分,例如不相关的列等。 - 一般来说,代码比尝试自己解释更精确。 :)

标签: java hibernate bulkinsert


【解决方案1】:

这有点难以猜测可能是什么问题,但我可以想到 3 件事:

  • 仅增加 batch_size 可能无济于事,因为 - 根据您的模型 - 插入可能是交错的(即 A B A B ...)。您可以允许 Hibernate 对插入和更新重新排序,以便可以对它们进行批处理(即 A A ... B B ...)。

    根据您的模型,这可能不起作用,因为插入可能无法批处理。必要的属性是hibernate.order_insertshibernate.order_updates,可以在此处找到描述这种情况的博客文章:https://vladmihalcea.com/how-to-batch-insert-and-update-statements-with-hibernate/

  • 如果实体不存在(似乎是这种情况),那么问题可能出在一级缓存上。这个缓存将导致 Hibernate 变得越来越慢,因为每次它想要刷新更改时,它都会通过迭代它们并调用equals()(或类似的东西)来检查缓存中的所有条目。正如您所看到的,创建的每个新实体都需要更长的时间。

    要解决这个问题,您可以尝试禁用一级缓存(我必须查看这是否可以用于写入操作以及如何这是完成 - 或者你这样做:))或尝试保持缓存小,例如通过自己插入书籍并在插入后从一级缓存中逐出每本书(您也可以更深入地在文档或段落级别执行此操作)。

  • 它实际上可能不是 Hibernate(或至少不是单独的),而是您的数据库。请注意,恢复转储通常会删除/禁用约束检查和索引以及其他优化,因此将其与 Hibernate 进行比较并没有那么有用。您需要做的是创建一堆插入语句,然后在空数据库上执行这些语句(理想情况下是通过 JDBC 批处理),但启用所有约束和索引。这将提供更准确的基准。

    假设比较表明普通 SQL 插入并没有那么快,那么您可以决定要么保留到目前为止的内容,要么重构批量插入以暂时禁用(或删除并重新创建)约束和索引。

或者,您可以尝试完全不使用 Hibernate 或更改您的模型 - 如果可能的话,考虑到您的要求,我不知道。这意味着您可以尝试自己生成和执行 SQL 查询,在支持它的 SQL 数据库(如 Postgres)中使用 NoSQL 数据库或 NoSQL 存储。

我们正在做类似的事情,即我们有 Hibernate 实体,其中包含一些存储在 JSONB 列中的复杂数据。 Hibernate 可以通过自定义用户类型读取和写入该列,但它不能过滤(Postgres 会支持这一点,但我们没有设法在 Hibernate 中启用必要的语法)。

【讨论】:

  • 感谢这些线索。我会阅读你给我的链接。我在第一篇文章中添加了一些信息,说明我认为我发现了什么样的问题......谢谢:-D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-06
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多