Amazon Elasticsearch - 并发批量请求

Amazon Elasticsearch - Concurrent Bulk Requests

当我通过一个批量请求将 200 个文档添加到 ElasticSearch 时 - 它非常快。

但我想知道是否有机会通过并发执行来加快进程:20 个并发执行,每个执行 10 个文档。

我知道效率不高,但也许有机会通过并发执行来加快进程?

这个问题没有直接的答案,因为它取决于很多因素。在最佳批量请求大小之上,性能不再提高甚至可能下降。然而,最佳尺寸不是一个固定的数字。

这完全取决于您的硬件、文档大小和复杂性以及索引和搜索负载。

尝试以越来越大的规模为典型文档编制索引。当性能开始下降时,您的批量大小太大了。

由于您是以 200 个为一批进行的,因此它很可能是最佳的索引方式。但这又取决于上述因素。

对于批量文档插入,较低的并发性更可取。某些并发性在某些情况下是有帮助的 — It Depends™,我将深入探讨 — 但不是主要或自动获胜。

在写入 Elasticsearch 的性能方面有很多可以调整的地方。您应该检查一个非常快速的胜利:您是否为您的连接使用 HTTP keep-alive?这将节省大量建立每个连接的 TCP 和 TLS 开销。正是这一变化可以显着提高性能,并为您的索引管道揭示一些有意义的架构注意事项。

所以检查一下,看看进展如何。从那里开始,我们应该走到底部,然后向上爬。

磁盘上的索引是Lucene。 Lucene 是一个分段索引。 index 部分是您首先使用 Elasticsearch 的核心原因:可以在 O(log N) 时间内搜索已排序术语的字典。这是超级快速和可扩展的。段部分是因为插入到索引中不是特别快 - 根据您的实现,维护排序的成本为 O(log N) 或 O(N log N)。

所以 Lucene 的技巧是缓冲这些更新并附加一个新段;本质上是迷你指数的集合。搜索一些相对较少的段仍然比每次更新都花时间维护排序索引要快得多。随着时间的推移,Lucene 负责 合并 这些段以将它们保持在一些合理的大小范围内,并在此过程中清除已删除和覆盖的文档。

在 Elasticsearch 中,每个分片都是一个不同的 Lucene 索引。如果您有一个包含单个分片的索引,那么拥有多个并发的批量更新流几乎没有什么好处。应用程序端的并发可能有一些好处,具体取决于索引管道收集所需的时间和 assemble 每批文档。但在 Elasticsearch 方面,这只是一组缓冲区被一个接一个地写出到一个段。

分片使这更有趣。

Elasticsearch 的优势之一是能够分区 跨多个分片的索引数据。这有助于提高可用性,并帮助工作负载扩展到单个服务器的资源之外。

唉,要说并发性应该与索引所具有的主分片数相等或成正比,并不是那么简单。虽然,作为一种粗略的启发式方法,这并不是一个糟糕的方法。

你看,在内部,处理请求的第一个 Elasticsearch 节点会将批量请求转换为一系列单独的文档更新操作。每个文档更新都会发送到托管该文档所属分片的适当节点。响应由批量操作收集,以便它可以在对客户端的响应中发送批量操作的摘要。

因此,此时,根据文档分片路由,在处理传入批量请求的过程中,某些分片可能比其他分片更忙。这可能 很重要吗? 我的直觉告诉我这不是真的。有可能,但不常见。

在我见过的大多数测试和分析中,以及在我使用 Lucene 大约十年的经验中,索引的缓慢部分是将文档的值转换为倒排索引格式。解析文本、将其分析成术语等可能非常复杂且成本高昂。只要批量请求具有足够多的文档,这些文档在分片之间分布得很好,并发性就不如在分片和段级别完成的工作饱和那么有意义。

调整批量请求时,我的建议是这样的。

  • 使用 HTTP 保活。这不是可选的。 (您正在使用 TLS,对吗?)
  • 选择每个请求花费适度时间的批量大小。大约 1 秒,可能不会超过 10 秒。
  • 如果你能想象得到,测量每个批量请求花费的时间,并动态地增加和缩小你的批次。

持久队列解锁了很多功能。如果可以获取和 assemble 文档并将它们插入到 Kafka 中,那么该过程可以 运行 并行以使数据库饱和并并行化任何反规范化或文档准备。然后一个不同的进程从队列中拉出并向服务器发送请求,通过一些简单的协调,您可以在不同阶段测试和调整不同的并发性。当队列有助于将集群暂时置于只读模式时,它还允许您暂停各种迁移和维护任务的更新。

我在整个回答过程中都避免了复制,因为只有一个原因让我建议调整复制。那就是当您批量创建不为任何生产流量提供服务的索引时。在这种情况下,关闭对索引的所有复制并在索引基本上完成加载数据后启用复制可以帮助通过服务器队列节省一些资源。

最后,如果你提高并发性怎么办?有什么风险?一些工作负载不控制并发性,并且没有时间或资源在搜索引擎前面放置一个队列。在这种情况下,Elasticsearch 可以避免大量的并发。它有相当大的线程池来处理并发文档更新。如果这些线程池已饱和,它将拒绝响应并显示 HTTP 429 错误消息和关于队列深度已超出的明确消息。这些会影响集群的稳定性,具体取决于可用资源和索引中的分片数量。但这些都是非常明显的问题。

底线:不,相对于 1 个包含 200 个文档的批量,每个包含 10 个文档的 20 个并发批量可能不会加快性能。如果您的批量操作很快,您应该增加它们的大小,直到它们 运行 持续一两秒钟,或者出现问题。使用保活。如果存在其他应用程序端开销,请将并发性增加到 2 倍或 3 倍并根据经验进行衡量。如果索引是关键任务,请使用快速、持久的队列。