ES 5.4+ 引起的Kibana性能问题

原载于Elastic中文社区: https://elasticsearch.cn/article/269

上周有用户在社区发了一例Kibana读取超时的问题:question#2319 。周末找时间帮其调查了下,发现某些较新的ES版本和Kibana搭配,会产生意想不到的缓慢问题。 考虑到这个问题比较普遍,因此在这里总结一下问题的根源和解决办法,希望用到问题版本的用户不要踩到坑。

首先问题的现象在上面的问题链接里有描述,简而言之就是对于一个硬件配置比较高的集群,每天写入一个20亿左右数据的索引,通过kibana的discovery面板查看数据会一直超时。即使时间范围放到最近半小时,超时依旧,有些蹊跷。

周末拿到用户给的测试账号,登陆集群看了下状态。 从机器的硬件配置,集群和索引的配置看,没找到什么特别不对劲的地方。然而点击到Discovery面板,的确数据显示不出来。 集群监控数据看,并没有其他用户在做查询,cpu利用率和集群负载都比较低。因此初步可以判定,就是查询本身比较缓慢所致。

对于诊断查询缓慢问题,我通常的做法,就是将对应面板下的查询拷贝出来,在Kibana Dev Console里手动执行,然后再加上"profile":true选项,看看查询是如何解析和执行的。对应的查询形如下面这样:

{
  "profile": true,
  "query": {
    "bool": {
      "must": [
        {
          "query_string": {
            "analyze_wildcard": true,
            "query": "*"
          }
        },
        {
          "range": {
            "@timestamp": {
              "gte": "now-1h",
              "lte": "now",
              "format": "epoch_millis"
            }
          }
        }
      ]
    }
  }
}

因为用户query框什么都没有输入,因此默认查询串被Kibana设置为*, 然后根据选择的时间范围加了一个range查询。 profile的输出让我稍微有些吃惊,其中 query_string的里的*居然被解析成非常复杂的DisjunctionMaxQuery,主要查询耗时都在这里了。

{
                    "type": "DisjunctionMaxQuery",
                    "description": "(ConstantScore(_field_names:remote_addr.keyword) | ConstantScore(_field_names:geoip.country_isocode) | ConstantScore(_field_names:geoip.country_name.keyword) | ConstantScore(_field_names:via) | ConstantScore(_field_names:domain.keyword) | ConstantScore(_field_names:request_method.keyword) | ConstantScore(_field_names:protocol) | ConstantScore(_field_names:xff.keyword) | ConstantScore(_field_names:host) | ConstantScore(_field_names:geoip.city_name.keyword) | ConstantScore(_field_names:client_ip) | ConstantScore(_field_names:host.keyword) | ConstantScore(_field_names:geoip.longitude) | ConstantScore(_field_names:geoip.subdivision_name.keyword) | ConstantScore(_field_names:geoip.country_code) | ConstantScore(_field_names:upstream_addr.keyword) | ConstantScore(_field_names:@version.keyword) | ConstantScore(_field_names:request_uri) | ConstantScore(_field_names:tags) | ConstantScore(_field_names:idc_tag) | ConstantScore(_field_names:size) | ConstantScore(_field_names:http_referer) | ConstantScore(_field_names:message.keyword) | ConstantScore(_field_names:domain) | ConstantScore(_field_names:geoip.latitude) | ConstantScore(_field_names:xff) | ConstantScore(_field_names:protocol.keyword) | ConstantScore(_field_names:geoip.country_code.keyword) | ConstantScore(_field_names:status) | ConstantScore(_field_names:upstream_addr) | ConstantScore(_field_names:http_referer.keyword) | ConstantScore(_field_names:tags.keyword) | ConstantScore(_field_names:client_ip.keyword) | ConstantScore(_field_names:request_method) | ConstantScore(_field_names:upstream_status) | ConstantScore(_field_names:request_time) | ConstantScore(_field_names:geoip.location) | ConstantScore(_field_names:@version) | ConstantScore(_field_names:geoip.country_name) | ConstantScore(_field_names:user_agent) | ConstantScore(_field_names:idc_tag.keyword) | ConstantScore(_field_names:remote_addr) | ConstantScore(_field_names:geoip.country_isocode.keyword) | ConstantScore(_field_names:geoip.city_name) | ConstantScore(_field_names:via.keyword) | ConstantScore(_field_names:message) | ConstantScore(_field_names:user_agent.keyword) | ConstantScore(_field_names:request_uri.keyword) | ConstantScore(_field_names:@timestamp) | ConstantScore(_field_names:upstream_response_time) | ConstantScore(_field_names:geoip.subdivision_name))",
                    "time": "5535.127008ms",
                    "time_in_nanos": 5535127008

也就是说, ES将只含一个*query_string query解析成了针对mapping里能找到的所有字段的field:*查询,然后合并所有的查询结果。 可想而知,对于比较大,字段比较多的索引这个查询是非常耗时的。而我对于*的认知,是其应该被rewrite成一个match_all query即可,这样几乎没有什么开销。

为什么会这样? 查询了一下ES官方关于Query String Query的文档,其中的default_field和all_fields起到了一定作用:
elasticsearch/reference/5.5/query-dsl-query-string-query.html

default_field

The default field for query terms if no prefix field is specified. Defaults to the index.query.default_field index settings, which in turn defaults to _all.

all_fields

Perform the query on all fields detected in the mapping that can be queried. Will be used by default when the _all field is disabled and no default_field is specified (either in the index settings or in the request body) and no fields are specified.

根据解释,查询的时候可以带一个default_field选项,其默认值为索引级别设置index.query.default_field,如果这个设置没有设置,则默认为_all。 但一般用户索引日志的时候,都会关掉_all字段,用于节省磁盘空间,提升索引速率。那么这时候default_field是什么呢? 答案是all_fields,也就是ES会将查询转换为对所有字段的查询。

为了验证这个是问题所在,我在索引里加了一个default_field的设置,随意挑选了一个字段。 果然问题就解决了,discovery面板渲染速度快了差不多有10倍。

但仔细想想,这也只是绕过了问题。 问题的根源,为什么*不被rewrite成match_all呢?

这时候想到我们自己生产的集群似乎没有这个问题,于是用我们自己的集群测试了一下,*果然是正常解析成match_all了。 于是对比了一下集群ES的版本,我们正常工作的是5.3.2,用户的集群是5.5.0

接下来,我想找到这些版本之间,ES对于query string的解析源码层面做了什么改动。经过一番探查,找到了下面这个变更历史:



可以看到,在pull/23433里,为了修复一个foo:*解析歧义的问题,对于未指定field名称,光提供一个*的Query string查询,不再被解析成match_all了,而是扩展成全部字段的DisjunctionMaxQuery查询。 由此Kibana默认的*,会引起非常严重的性能问题。

注意: 这个问题会影响5.4和5.5两个小版本的ES/Kibana。

顺着这个issue里的链接摸下去,找到了对应Kibana相关问题讨论:issues#12097,以及对应的修复: pull/13047,修复版本默认发出的查询串是match all

修复的版本则是5.5.25.6.0, 因此有用到5.4.05.5.1之间版本的ELK用户一定要安排升级!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容

  • 欢迎访问我的博客查看原文:http://wangnan.tech 注:文本整理自《ELKstack权威指南》 目录...
    GhostStories阅读 10,419评论 0 7
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • 一、环境搭建 软件版本: Linux:centos7 64bit JDK:1.8.0 elasticsearch:...
    lailai900201阅读 3,580评论 0 12
  • 博客原文一博客原文二 翻译作品,水平有限,如有错误,烦请留言指正。原文请见 官网英文文档 起步 Elasticse...
    rabbitGYK阅读 3,256评论 0 68
  • 早上与先生上班路上聊到教育与学习, 有些人觉得自己身边不读书而很有钱的人很多呢?所以自己当年那么辛苦学习,希望现在...
    宸世间阅读 182评论 1 0