如何使用休眠优化一个大插件

时间:2019-07-05 14:45:13

标签: java hibernate bulkinsert

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

我试图增加batch_size。但这并没有改变,因为它是唯一的保存。当我mysqldump一个脚本,然后将其重新插入mysql时,该操作将在2分钟或更短时间内完成。 当我在ubuntu计算机上执行“ htop”操作时,我可以看到mysql仅使用2%或3%的CPU。这意味着谁很慢都处于休眠状态。

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

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

第二个问题还是关于外键的问题。 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();

1 个答案:

答案 0 :(得分:1)

很难猜测这里可能是什么问题,但我可以想到三件事:

  • 仅增加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查询,使用NoSQL数据库或支持SQL的SQL数据库中的NoSQL存储-例如Postgres。

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