如何排查 Elasticsearch 慢查询以改善用户体验

Elastic Cloud (Elasticsearch Service/ESS) 用户的重要注意事项:目前,本文中提到的内容在 Elastic Cloud 上不可用。不过,我们希望收集我们宝贵用户的声音。如果您有兴趣使用此功能,请联系 Elastic 支持。

对于任何使用 Elasticsearch® 作为搜索引擎的用户来说,识别和排查查询问题都是必须掌握的关键技能。无论是电子商务、可观测还是面向工作场所的搜索解决方案,Elasticsearch 运行缓慢都会对您的用户体验产生负面影响。

要查明缓慢的 Elasticsearch 查询,您可以使用 慢速日志 (slow log),它会捕获在特定阈值下运行的查询。正确设置慢速日志阈值本身就是一个挑战。例如,在满负载下耗时 500 毫秒的查询可能是可以接受的,但在低负载下同样的查询可能就无法接受。慢速日志不会进行区分,而是会记录所有超过 500 毫秒的查询。慢速日志非常出色,因此您可以根据阈值捕获不同粒度的数据。相比之下,追踪 (Tracing) 可以查看所有查询,识别出有多少查询处于特定阈值范围内。

应用程序性能监测 (APM) 不再局限于您的应用程序。利用 Elasticsearch 中的检测功能,我们现在可以将 Elasticsearch 作为一项成熟的服务添加,而不是作为应用程序堆栈的依赖项。通过这种方式,我们能够获得比慢查询日志所能提供的更细致的性能视图。

对于以下示例,我们的数据语料库是 OpenWebText,它提供了约 40GB 的纯文本和约 800 万个独立文档,这些文档在配备 32GB RAM 的 M1 Max Macbook 上本地运行。

开始使用

在 Elasticsearch 中激活追踪功能需要使用静态设置(在 elasticsearch.yml 中配置)和动态设置,后者可以使用 PUT _cluster/settings 命令在运行时进行切换,其中一个动态设置就是采样率。某些设置(如采样率)可以在运行时进行切换。在 elasticsearch.yml 中,我们希望设置以下内容:

适用于 9.x 版本

telemetry.agent.enabled: true
telemetry.agent.server_url: "url of the APM server"

适用于 7.x 和 8.x 版本

tracing.apm.enabled: true
tracing.apm.agent.server_url: "url of the APM server"

密钥令牌(或 API 密钥)必须位于 Elasticsearch 密钥库中。密钥库工具应位于<your elasticsearch install directory> ` /bin/elasticsearch-keystore` 目录下。 对于 7.x 和 8.x 版本,请使用以下命令 添加密钥:`elasticsearch-keystore add tracing.apm.secret_token or tracing.apm.api_key` 。 对于 9.x 版本,请改用 `telemetry.secret_token` `telemetry.api_key` 之后,您需要重启 Elasticsearch。有关跟踪的更多信息,请参阅我们的 跟踪文档

一旦 APM 激活,我们就可以查看 Kibana 中的 APM 视图,并看到 Elasticsearch 会自动捕获各种 REST API 终端。在这里,我们主要关注 POST /{index}/_搜索 调用,并看看能从中获得什么。

Elasticsearch 屏幕截图

通过直接在 GET /{index}/_搜索 框中检查一个简单的查询,我们可以看到以下瀑布式分解。其中包含一些内部跨度,可以提供更深入的见解,了解 Elasticsearch 在底层执行的操作。我们还可以看到此搜索的总持续时间(86 毫秒)。

跟踪样例

随查询附带的元数据包含有关 HTTP 标头、用户代理、Elasticsearch Node 位置(云服务提供商元数据、主机名、容器信息)、部分系统信息以及 URL 详情的广泛信息。利用一些基本的事务信息,我们可以创建一个 Lens 图表,绘制平均事务持续时间,并借此观察是否存在上升或下降趋势。

我们的搜索应用程序

不再需要使用慢日志,这真是太好了!我可以确定事务持续时间,并识别有多少搜索是在任何阈值之下得到响应的。然而,有一个缺点——Elasticsearch 不会捕获所发送的查询,因此我们知道某个查询耗时很长,但我们不知道该查询具体是什么。

让我们检测一个示例搜索应用。在此案例中,我们将使用一个简单的 Flask 应用,它包含两个路由 search_singlesearch_phrase,它们分别代表 Elasticsearch 中的 matchmatch_phrase 查询。例如,我们可以使用以下查询:

{
  "query": {
    "match": {
      "content": "support"
    }
  }
}
And
{
  "query": {
    "match_phrase": {
      "content": "support protest"
    }
  }
}

以下 Flask 代码实现了 search_single 路由。search_phrase 非常相似,只是它使用 match_phrase 而不是 match

@app.route("/search_single", methods=["GET"])
def search_single():
    query = request.args.get("q", "")
    if not query.strip():
        return jsonify({"error": "No search query provided"}), 400
    try:
        result = es.search(
            index=ES_INDEX, query={"match": {"content": query}}
        )

        hits = result["hits"]["hits"]
        response = []
        for hit in hits:
            response.append(
                {
                    "score": hit["_score"],
                    "content": hit["_source"]["content"],
                }
            )
        
        return jsonify(response)

准备好所有这些后,我现在可以调用 curl -XGET "http://localhost:5000/search_single?q='microphone'" 来搜索术语 microphone

我们主要将 APM 添加到我们的搜索应用程序中以进行 Observe,但我们的 APM 代理会捕获传出请求并使用元数据信息对其进行丰富。在我们的案例中,span.db.statement 包含 Elasticsearch 查询。在下面的这个案例中,有人搜索了 window

跨度详情

将所有内容整合到一起

在我的 Flask 服务中,我将查询大小设置为 5,000,这意味着 Elasticsearch 应该在单个 JSON 响应中为我提供最多 5,000 个匹配文档。这是一个很大的数字,而且大部分时间都花在了从磁盘检索这些文档上。将其更改为前 100 个文档后,我可以通过比较快速识别仪表板中发生的情况。

在 APM 视图中查看事务并激活关键路径的实验室功能会创建一个叠加层,向我们展示应用程序在哪些位置运行耗时。

APM 时间线视图

之后,我使用 transaction.duration.uses_query_tooktransaction.name 字段创建了一个仪表板。常规 KQL 过滤器包含 service.nameprocessor.event: transactiontransaction.name: POST /{index}/_搜索

小贴士: 前往 Data view 管理 > 选择包含 APM 数据流的 Data view > 选择 transaction.duration.us 字段 > 并将格式更改为 duration。现在,它将自动以人类可读的输出呈现,而不是以微秒为单位。

利用 Lens 注释功能,我们可以从中间的 Lens 中看到,将文档数更改为 100 后,平均搜索事务处理量大幅下降。不仅如此,请查看右上角的记录总数。由于我们可以更快地进行搜索,因此我们拥有更高的吞吐量!我非常喜欢直方图,因此我在顶行的中间创建了一个直方图,其中 X 轴为事务持续时间,Y 轴为记录数。此外,APM 还提供指标,因此我们可以随时确定 CPU 的使用百分比,以及 JVM 堆、非堆使用情况、线程数和其他有用的信息。

图形和图表

结论

本篇博客文章向您展示了将 Elasticsearch 作为检测应用程序是多么重要,以及如何更轻松地识别瓶颈。此外,您还可以将事务持续时间作为异常检测的指标,对您的应用程序进行 A/B 测试,并且再也不用纠结 Elasticsearch 是否变快了,因为您现在有了数据来回答这个问题。此外,从用户代理到查询所收集的所有元数据都有助于您进行故障排查。

仪表板和 Data view 可从此处导入。

警告

Elasticsearch 内部的事务持续时间存在一个问题。此问题已在 即将发布的 8.9.1 版本中修复。在此之前,事务会使用错误的时钟,这会干扰整体持续时间。

本文中描述的任何功能或功能性的发布和时间均由 Elastic 自行决定。当前尚未发布的任何功能或功能性可能无法按时提供或根本无法提供。