Hibernate Search:将来自单个 table 的 5000 万行重新索引到 Elastic Search

HibernateSearch : Reindex 50 million rows from a single table into Elastic Search

我们目前正在使用 Mass Indexer 的默认设置(每个查询、每个线程加载 10 个对象)和 7 个线程将数据从 1 table(8-10 个字段)重新索引到弹性搜索。 table 的规模目前为 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.

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

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

UPDATE: 而且 IDLoader 线程似乎没有批处理,所以对于 5000 万行,它将在 1 个查询中加载内存中的所有 5000 万个 ID?

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

What is the best strategy to reindex very large datasets? Can MassIndexer scale to this high volume with some configuration settings?

有那么多实体,事情肯定不会只花几分钟。

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

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

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

我建议首先集中精力解决延迟加载问题,因为这些问题会对性能产生巨大影响;务必设置hibernate.default_batch_fetch_size以减少延迟加载对性能的影响

那么,我只能重复 what the reference documentation says:

The MassIndexer was designed to finish the re-indexing task as quickly as possible, but there is no one-size-fits-all solution, so some configuration is required to get the best of it. Performance optimization can get quite complex, so keep the following in mind while you attempt to configure the MassIndexer:

  • Always test your changes to assess their actual effect: advice provided in this section is true in general, but each application and environment is different, and some options, when combined, may produce unexpected results.
  • Take baby steps: before tuning mass indexing with 40 indexed entity types with two million instances each, try a more reasonable scenario with only one entity type, optionally limiting the number of entities to index to assess performance more quickly.
  • Tune your entity types individually before you try to tune a mass indexing operation that indexes multiple entity types in parallel.

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

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


Or should we look at other strategies?

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

如果您不相信 MassIndexer 能做好,您可以尝试自己做。设置一个线程加载ID,其他线程加载对应实体,然后index them manually。要做到这一点并不简单,但这是可能的。

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

  • 如果延迟加载似乎是问题所在,您可以使用 entity graphs to make sure all parts of your entity that are indexed will be loaded eagerly. The MassIndexer cannot currently do that, though hopefully it will someday (HSEARCH-521).
  • 如果有一些 JDBC 查询提示可以提高您的情况的性能,您可以尝试设置它们。
  • 如果它能够处理负载,并且瓶颈似乎是将实体处理成文档,那么您可以尝试将 ID 和 运行 您的“自定义索引过程”分割成多个机器。例如。在一台机器上重新索引 1 到 25,000,000 的 ID,在另一台机器上重新索引 25,000,001 到 50,000,000 的 ID。您不能使用质量索引器执行此操作,因为它不允许过滤 ID(至少在 Hibernate Search 6.0 中不行,但在 6.1 中可以:HSEARCH-499)

UPDATE: Also it looks like the IDLoader thread is not batched, so for 50 million rows, it will load all 50 million IDs in memory in 1 query?

不是,id是批量加载的。然后将每个批次推送到内部队列,并由加载线程使用。批次的大小由 batchSizeToLoadObjects.

控制

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

有关参数的更多信息 here


And, what is the use of idFetchSize? Looks like it is not used in the indexing process.

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

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