Elasticsearch 慢查询自动化巡检实践

Elasticsearch 作为一个检索查询的文档数据库,在查询统计方面有着较为自由且丰富的方式方法。在满足各类查询聚合功能的同时,也会有着资源性能使用的浪费或者误区。在功能和性能使用中寻求最优解,诞生了 ES 慢查询治理这个场景问题。本文主要介绍了 ES 慢查询自动化巡检治理的具体内容和基础逻辑,并对巡检中的判断规则进行解读和相应的解决建议。

一、慢查询巡检逻辑


1. 慢查询 DSL 获取

慢查询 DSL 的获取主要依赖于慢查询日志的清洗聚合。主要有下面几个操作:

1.1 日志信息要素清洗

这里通过程序脚本从腾讯云同步日志信息到本地的 ES 集群,并根据日志类型进行拆分要素。

拆分主要利用 ES 的 ingest pipeline。grok表达式:

"[%{DATA:classname}] [%{DATA:node}] [%{DATA:indexname}][%{NUMBER:shard_no}] took[%{DATA:took}], took_millis[%{NUMBER:took_millis}], total_hits[%{DATA:hits}], types[%{DATA:types}], stats[%{DATA:stats}], search_type[%{DATA:search_type}], total_shards[%{NUMBER:total_shards}], source[%{DATA:Message}], id[%{DATA:id}], "

可以获得的日志结构如下:

{
    "logType": "SearchSlow",
    "took": "271.7ms",
    "total_shards": "12",
    "types": "_doc",
    "took_millis": "271",
    "Message": "{"size":1000,"query":{"bool":{"must":[{"term":{"type1":{"value":"type1","boost":1.0}}},{"term":{"id":{"value":123,"boost":1.0}}}],"must_not":[{"terms":{"tag1":["tag1","tag2","tag2"],"boost":1.0}}],"adjust_pure_negative":true,"boost":1.0}},"_source":false}",
    "Ip": "9.20.83.243",
    "shard_no": "4",
    "Cluster": "test-online",
    "Time": "2022-06-27T22:34:59.428+08:00",
    "search_type": "QUERY_THEN_FETCH",
    "hits": "-1",
    "node": "node1",
    "classname": "i.s.s.fetch              ",
    "indexname": "test_fetch",
    "stats": "",
    "Level": "WARN",
    "id": ""
}

主要字段:

其中对后面巡检判断比较有用的是,took耗时、message dsl信息、classname 查询类型、集群名/索引名。清洗后的结果如图:

此时的结果还只是明细数据,微盟一百多个集群每天产生的慢日志数量非常庞大,只是通过 kibana 进行检索信息并不能有效的治理慢查询。这时就需要对慢日志数据进行进一步聚合。

1.2 DSL 聚合

DSL 聚合的目的是为了将慢日志中的 DSL 去重,保留有效且单一的 DSL。慢日志中每条 DSL 都保留了传入参数,这就造成了简单的聚合并不能获取唯一的 DSL 内容,即实际的 DSL 查询条件。比如下面的查询我们可以认为是一个 DSL 内容,只是传入的参数不一样:

{"query":{"term":{"_id":{"value":"1"}}}}

{"query":{"term":{"_id":{"value":"max"}}}}

因此我们使用脚本进行正则匹配把传入参数消除,并通过生成 MD5 进行唯一性校验。

# 正则匹配去除 dsl 中的传入参数
pattern = r'("max_expansions":)"[0-9]+"'
replacement = r'1""'
query_dsl = re.sub(pattern, replacement, query_dsl)
# 生成 md5 作为 dsl 的唯一性 id

str_md5 = hashlib.md5(cluster_index_query.encode(encoding='UTF-8')).hexdigest()

最终效果如下:

```
{"size":"","query":{"bool":{"must":[{"term":{"id1":{"value":"",}}},{"terms":{"tag_id":[""],}}],"adjust_pure_negative":true,}},"aggregations":{"by_tag_id":{"terms":{"field":"tag_id","size":"",,,"show_term_doc_count_error":false,"order":[{"order1":"desc"},{"_key":"asc"}]},"aggregations":{"count_id":{"value_count":{"field":"id"}}}}}}
```

在获取实际 DSL 查询条件的同时,我们也保留了DSL 明细与耗时,通过取耗时中位数来保留带入参数的 DSL。接下来,我们利用 ES/lucene 提供的 profile 工具进行解析判断。

2. profile 解析要点

2.1 profile 基础知识

Profile API 让用户深入了解搜索请求是如何在低级别执行的,以便用户可以理解为什么某些请求很慢,并采取措施来改进它们。请注意, 除其他外,配置文件 API 不会测量网络延迟、搜索获取阶段花费的时间、请求在队列中花费的时间或在协调节点上合并分片响应时花费的时间。主要的结构如下:

{
    "profile": {
        "shards": [
            {
                "id": "[2aE02wS1R8q_QFnYu6vDVQ][my-index-000001][0]",
                "searches": [
                    {
                        "query": [...],    # query 阶段的耗时分析
                        "rewrite_time": 51443, # 对 dsl 解析重写的耗时
                        "collector": [...] # collector 阶段的耗时
                    }
            ],
             "aggregations": [...] # 聚合阶段的耗时
            }
        ]
    }
}

在 profile 反馈的结果中,包含整个 search 阶段和各个子阶段。

对于不同版本的 ES, profile API 提供的信息内容也略有不同。这里基于当前主要使用的 7.10 版本,有以下内容是 profile 没有提供的:


  1. profile 分析目前不测量 fetch 阶段和网络开销。
  2. 分析也不考虑队列中花费的时间、合并协调节点上的分片响应或其他耗时,如构建全局序号。
  3. 目前无法用于 suggestions、高亮 highlighting、dfs_query_then_fetch。
  4. aggregation 的 reduce 阶段的分析目前不可用。


具体的 profile 使用细则可以参考官网:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/search-profile.html 总体来说,profile 提供了尽可能详细的 ES 查询逻辑解析, ES 使用者可以通过其中的内容进行可靠的查询优化。我们可以通过下面两则逻辑进行对 DSL profile 内容进行自动化的分析:

2.2 profile 与慢日志内容的结合

日志记录了 DSL 查询的明细,profile 提供了 DSL 的执行详情,仿佛两者相结合就能方便的判断出 DSL 的不合理之处。

但其实没那么简单,profile的本质是一个lucene collector,所以仅仅统计lucene 耗时,不包含任何网络交互式,队列等待。慢日志实际上是某个分片查询达到数据节点开始计时的。因此两者并不能完整的契合到一起。

所以也带来了下面的一些特殊处理:


  1. fetch 阶段的慢查询并不能从 profile 中获取相关信息,但由于 fetch 阶段处理逻辑相对简单,可以直接从慢日志中做类型判断。
  2. 由于 profile 的耗时是一个参考数据,profile 结果中较大的子查询耗时只能作为优化方向的依据,并不能直
  3. 接生成

  4. 需要优化的规则。
  5. DSL 的 took 耗时理论上
  6. 会大于 profile 各个子阶段的耗时总和,需要对两个数据进行计算,为 profile 未统计的因素提供优化依据。

其中 profile 与实际 took 耗时也会产生一些差异,比如:took 小于 profile 总和的情况,由此产生了耗时占比会变成负数。


具体官方解释参考这里:https://github.com/elastic/elasticsearch/issues/33489

3. DSL 巡检规则结果

每日慢查询巡检结果会根据集群写入同个 excel 文件,每个去除参数后的 DSL 作为一个单独的 sheet。

然后根据每个集群登记的联系人员 ldap,把 excel 邮件发送。

3.1 DSL 详情

这段主要展示了该 DSL 详情。


3.2 规则结果

巡检规则主要分以下三类:

  1. 需优化:慢查询巡检程序判断 DSL 中存在浪费性能的查询方式,需要改进该阶段。
  2. 待分析:慢查询巡检中发现 DSL 中某些阶段(包括大阶段和子阶段)的耗时不太合理,占比超过阈值,需对该阶段进行排查分析。
  3. 不需优化:展示了巡检统计的相关结果信息,但耗时占比未超过阈值。

3.3 profile 信息展示

profile 阶段的内容主要有下面几部分组成:

  1. 表格第一行为耗时最大的分片以及其耗时,这也是下面各个查询阶段数据的来源。
  2. 主阶段均大写,粉色底,无阶段描述。主阶段包括:rewrite_phase/query_phrase/collector_phase/aggs_phas,以及根据took与profile耗时总和相减得到的 other_phase。
  3. 子阶段名称均小写,黄色底,前面的星号为子阶段的层级,层级越深星号越多。比如图中“**PointRangeQuery”为“*BooleanQuery”的子阶段。

  4. 命中规则的阶段名称为红色,超过阈值的耗时占比为深红色。最终的统计数据则汇总进了运维使用的ES,进行分条件展示。




4. 需优化规则以及相关建议

ES/lucene 在底层的算法结构上,分别使用了 fst 去实现精确匹配和bkd tree 去实现范围查询。两者在dsl中的查询方式就是 term keyword 和 range long。

相对而已错误的查询方式就是 term long 和 range keyword。

4.1 规则 :存在 term long 查询

规则说明:

ES 中数值类型(代表为 long 类型)存储时并没有采用倒排表索引,而是以value为序,将docid切分到不同的block里面,这样处理可以利用Block k-d tree进行范围查找,速度非常快。但是满足查询条件的 docid 集合在磁盘上并非向 Postlings list 那样按照 docid 顺序存放,也就无法利用 BKD tree 的算法优势了。而此时要实现对 docid 集合的快速 advance 操作,只能将 docid 集合拿出来,然后构造成一个代表 docid 集合的 bitset ,在这个 bitset 上进行 advance 操作。如果命中结果集很大,这个代价会非常大,有可能会造成 cpu 跑满的现象。这里的 term long 查询则被转换为了 PointRangeQuery。

判断依据:

查询子阶段类型为 PointInSetQuery 或者 PointRangeQuery

处理建议:

  1. 修改类型,将数值型改成 keyword;
  2. 使用 range 方法进行查询;

4.2 规则:存在 range keyword/wildcard 查询

规则说明:

在 ES 中 keyword 类型是倒排结构的,在精确查询的场景下,fst 的查询算法能达到存储和性能的最优解。但是在范围查询的场景下,ES 的处理方式可以理解为简单的遍历,和 wildcard 查询一样,是极其耗费性能的。

判断依据:

查询子阶段类型为 MultiTermQueryConstantScoreWrapper

处理建议:

  1. 修改字段类型为数值型,比如 long。
  2. wildcard 查询的实现场景尝试 ngram 分词器或者 wildcard 类型

5. 总结

本文主要阐述了在大批量 ES 集群下进行慢查询治理的一个方案。在自动化巡检下,发现集群查询中可以优化的内容和方向,降低排查成本。其中慢查询的判断规则和优化手段也随着慢查询治理的深入而进一步完善。

在生产环境下慢查询的产生因素是复杂多样的。除了基本的 profile 分析,火焰图 /heap dump 等方式也是有效手段,在实际排查过程中也避免不了这类操作的介入。

面对不同的慢查询原因,优化手段也是因地制宜,往往也是需要研发运维甚至产品等各方面同学的支持。

作者:金端

来源:微信公众号:微盟技术中心

出处:https://mp.weixin.qq.com/s/SfT6EUOigpqsP0bjKCajEw

展开阅读全文

页面更新:2024-05-09

标签:阈值   集群   性能   规则   阶段   参数   类型   方式   内容   日志

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top