0

我们目前使用Mass Indexer7 个线程的默认设置(每个查询加载 10 个对象,每个线程)将 1 个表(8-10 个字段)中的数据重新索引到弹性搜索中。桌子的大小目前是25 million并将增长到几亿。

MassIndexer indexer = searchSession.massIndexer(Entity.class)
            .threadsToLoadObjects(7);

indexer.start()
     .thenRun(() ->
         log.info("Mass Indexing Entity Complete")
     )
     .exceptionally(throwable -> {
         log.error("Mass Indexing Entity Failed", throwable);
         return null;
     });

数据库是一个Postgres on RDS,我们正在使用AWS Elastic SearchHibernate Search版本是 6。

最近,我们在重新索引过程中遇到了瓶颈,因为它运行了几个小时,表中有 2000 万行。原因之一是我们有一个最多 10 个连接的连接池。使用当前的质量索引器设置(7 个线程),它只留下 2 个连接(1 个用于 Id 查找 + 7 个用于实体查找)用于导致超时等待连接的其他操作。我们将池大小增加到 20 并进行测试。

重新索引非常大的数据集的最佳策略是什么?MassIndexer 可以通过一些配置设置扩展到如此高的容量吗?还是我们应该看看其他策略?过去对具有相同要求的人有什么作用?

更新:看起来 IDLoader 线程没有批处理,所以对于 5000 万行,它会在 1 个查询中加载内存中的所有 5000 万个 ID?

还有,有什么用idFetchSize?看起来它没有在索引过程中使用。

4

1 回答 1

1

重新索引非常大的数据集的最佳策略是什么?MassIndexer 可以通过一些配置设置扩展到如此高的容量吗?

有了这么多实体,事情肯定会花费超过几分钟的时间。

它是否可以扩展......问题是,海量索引器只是您的数据库和 Elasticsearch 之间的中间人。假设您的数据库可扩展,并且 Elasticsearch 可扩展,那么海量索引器扩展所需的唯一事情就是并行执行更多工作。你可以控制它。

现在,您可能的意思是“它能否在令人满意的时间内重新索引”,这当然取决于您的期望是什么,以及您在调整它方面付出了多少努力。

海量索引的性能将受到您传递给海量索引器的配置的影响,当然,还会受到实体的架构和数据、RDBMS 及其配置、Elasticsearch 集群及其配置、它们运行的​​机器的影响, ... 真的,没有人知道什么是可能的:知道的唯一方法是尝试、评估结果、调整和迭代。

我建议首先专注于解决延迟加载问题,因为这些会对性能产生巨大影响;一定要设置hibernate.default_batch_fetch_size,以减少延迟加载对性能的影响。

然后,我只能重复参考文档所说的内容

MassIndexer 旨在尽快完成重新索引任务,但没有万能的解决方案,因此需要进行一些配置才能充分利用它。性能优化可能会变得相当复杂,因此在尝试配置 MassIndexer 时请记住以下几点:

  • 始终测试您的更改以评估其实际效果:本节中提供的建议通常是正确的,但每个应用程序和环境都不同,并且某些选项在组合时可能会产生意想不到的结果。
  • 采取婴儿步骤:在使用 40 个索引实体类型(每个类型为 200 万个实例)调整海量索引之前,尝试仅使用一种实体类型的更合理的方案,可选择限制要索引的实体数量以更快地评估性能。
  • 在尝试调整并行索引多个实体类型的海量索引操作之前,分别调整您的实体类型。

除了调整海量索引器之外,请记住它仅从数据库加载数据以将其推送到 Elasticsearch。可以肯定的是,海量索引器可能是瓶颈,但如果数据库或 Elasticsearch 尺寸过小,它们也可能是瓶颈。确保两者都能提供令人满意的吞吐量:体面的机器,必要时集群,服务器端配置,......

无论如何,你可以做很多事情:在你做之前,试着找出瓶颈是什么。您的数据库是否始终处于 100% CPU 状态?然后调整您的数据库:更改设置,使用更强大的机器,... Elasticsearch I/O 是否明显达到了极限?然后调整 Elasticsearch:更改设置,添加更多节点,...... Postgresql 和 Elasticsearch 都做得很好吗?那么也许你应该在你的海量索引器中有更多的数据库连接,或者更多的 ES 连接,或者更多的线程。或者也许是别的东西;性能很难。


还是我们应该看看其他策略?

我会把它作为最后的手段。如果您不了解质量索引器的性能究竟出了什么问题,那么您不太可能找到更好的解决方案。

如果您不相信 MassIndexer 会做得很好,您可以尝试自己做。设置一个加载 ID 的线程,以及加载相应实体的其他线程,然后手动索引它们。要做到这一点并不容易,但这是可能的。

如果你这样做,我怀疑你会改善任何事情。但是,假设实体加载是瓶颈,而不是索引(你必须先检查一下!),我想你可以通过利用数据库的细节来获得更好的吞吐量:

  • 如果延迟加载似乎是问题所在,您可以使用实体图来确保被索引的实体的所有部分都将被急切地加载。MassIndexer 目前无法做到这一点,但希望有一天它会(HSEARCH-521)。
  • 如果有一些 JDBC 查询提示可以在您的情况下提高性能,您可以尝试设置它们。
  • 如果它能够处理负载,并且瓶颈似乎是将实体处理为文档,那么您可以尝试对 ID 进行分区并在多台机器上运行您的“自定义索引过程”。例如,在一台机器上重新索引 ID 1 到 25,000,000,在另一台机器上重新索引 ID 25,000,001 到 50,000,000。您无法使用质量索引器执行此操作,因为它不允许过滤 ID(至少在 Hibernate Search 6.0 中不允许,但在 6.1: HSEARCH-499中会)

更新:看起来 IDLoader 线程没有批处理,所以对于 5000 万行,它会在 1 个查询中加载内存中的所有 5000 万个 ID?

不,ID 是分批加载的。然后每个批次被推送到一个内部队列,并由加载线程消耗。批次的大小由 控制 batchSizeToLoadObjects

一个例外是 MySQL,它的默认配置是将整个结果集加载到内存中(不要问我为什么),但这不会影响 PostgreSQL。无论如何,这可以修复(见下文)。

更多关于参数的信息在这里


还有,有什么用idFetchSize?看起来它没有在索引过程中使用。

这是 JDBC 提取大小。ID 是使用滚动(光标)检索的,JDBC 获取大小是 JDBC 驱动程序中此滚动的结果页面(~低级缓冲区)的大小。

老实说,它对 MySQL(也许还有 MariaDB?)最有用,即使我们使用游标,它的 JDBC 驱动程序也会将所有结果加载到内存中,除非获取大小设置为Integer#MIN_VALUE. 我知道,这很奇怪。

于 2021-07-22T07:36:34.367 回答