Elasticsearch搜索调优

最近把搜索后端从AWS cloudsearch迁到了AWS ES和自建ES集群。测试发现search latency高于之前的benchmark,可见模拟数据远不如真实数据来的实在。这次在产线的backup ES上直接进行测试和优化,通过本文记录search调优的主要过程。

问题1:发现AWS ES shard级别的search latency是非常小的,符合期望,但是最终的查询耗时却非常大(ES response的took), 整体的耗时比预期要高出200ms~300ms。

troubleshooting过程:开始明显看出问题在coordinator node收集数据排序及fetch阶段。开始怀疑是因为AWS ES没有dedicated coordinator节点,data node的资源不足导致这部分耗时较多,后来给所有data node进行来比较大的升级,排除了CPU,MEM, search thread_pool等瓶颈,并且通过cloud watch排除了EBS IOPS配额不够的可能,但是,发现search latency并没有减少。然后就怀疑是network的延时, 就把集群从3个AV调整到1个AV,发现问题依旧。无奈,联系了AWS的support,AWS ES team拿我们的数据和query语句做了benchmark,发现没有某方面的资源瓶颈。这个开始让我们很疑惑,因为在自建ES集群上search latency明显小于AWS ES,两个集群的版本,规格,数据量都差不多。后来AWS回复说是他们那边的架构问题,比之自建集群,AWS ES为了适应公有云上的security, loadbalance要求,在整个请求链路上加了一些组件,导致了整体延时的增加。

确定方案:限于ES cluster不受控,我们只能从自身的数据存储和查询语句上去优化。

存储优化:

1. index sort。我们的查询结果都是按时间(created_time)排序的,所以存储的时候即按created_time进行有序存储,方便提前中断查询。

2. segment merge。索引是按季度存储的,把2019年之前的索引进行了force merge,进行段合并,2019年之前的索引确定都是只读的。

3. 索引优化。合并了一些小索引,2016,2017年的数据量比较少,把这两年的索引进行合并,减少总shard数。通过创建原索引的别名指向新索引,保证search和index的逻辑不用改动。

查询优化。

先通过profile API定位耗时的子查询语句。

1. 合并查询字段。一个比较耗时子查询查询如下,通常session_id的list size>100,receiver_id和sender_id也会匹配到n多条记录。

{
    "minimum_should_match": "1"
    "should": [
        {
            "terms": {
                "session_id": [
                    "ab",
                    "cd"
                ],
                "boost": 1
            }
        },
        {
            "term": {
                "receiver_id": {
                    "value": "efg",
                    "boost": 1
                }
            }
        },
        {
            "term": {
                "sender_id": {
                    "value": "hij",
                    "boost": 1
                }
            }
        }
    ]
}

新开一个字段session_receiver_sender_id,通过copy_to把每条记录的session_id,receiver_id, sender_id都放到这个字段上。把query语句改为

{
    "terms": {
        "session_receiver_sender_id": [
            "ab",
            "cd",
            "efg",
            "hij"
        ],
        "boost": 1
    }
}

不过,测试结论显示,合并之后query耗时并没有明显缩短,感觉改动意义不大。推测可能是我们的BoolQuery字段并不多(就3个),但是terms的size很多(100以上),因为不管是多个字段每个对应一个termQuery,还是一个terms query, 都是转成BoolQuery,最终都是多个termQuery做or。

2. 优化date range查询。另外一个比较耗时的查询是date range。Lucene会rewrite成一个DocValuesFieldExistsQuery。

"filter": [
    {
        "range": {
            "created_time": {
                "from": 1560993441118,
                "to": null,
                "include_lower": true,
                "include_upper": true,
                "boost": 1
            }
        }
    },
    ...
]

这里匹配到的docId的确非常多,date range结果在构造docIdset与别的子查询语句做conjunction耗时较大。

采用的一个解决方案是尽量对这个子查询进行缓存,把这个date range查询拆成两段,分为3个月前到昨天,昨天到今天两段,一般昨天的数据不再变化,在没有触发segment merge的情况下3个月前到昨天到查询结果应该能缓存较长时间。

"constant_score": {
  "filter": {
    "bool": {
      "should": [
        {
          "range": {
            "created_time": {
              "gte": "now-3M/d",
              "lte": "now-1d/d"
            }
          }
        },
        {
          "range": {
            "created_time": {
              "gte": "now-1d/d",
              "lte": "now/d"
            }
          }
        }
      ]
    }
  }
}

相应的,在用户可接受的前提下,调大索引的refresh_interval。

问题2: 在自建ES集群上,发现某个索引500ms以上的搜索耗时占比较多。

这个索引每日大概30w次查询,落在100ms以内的查询超过90%,但是依旧有1%的查询落在500ms以上。发现同样的query语句模版,但如果某些子查询条件匹配到的数据比较多,查询会变对特别慢。

troubleshooting过程:同样是通过profile参数分析比较耗时的查询子句。发现一个PointInSetQuery非常耗时,这个子查询是对一个名为user_type的Integer字段做terms查询,子查询内部又耗时在build_score阶段。

通过查找lucene的代码和相关文章,发现lucene把numeric类型的字段索引成BKD-tree,内部的docId是无序的,与其他查询结果做交集前构造Bitset比较耗时,从而把Integer类型改成keyword,把这个查询转成TermQuery,这样哪怕命中的数据很多,在build_score的时候因为倒排链的docId有序性,利用skiplist,可以更快速的构建一个Bitset。在把这个字段改成keyword后,50th的查询耗时并没有多大差异,但是90th、99th的search latency明显小于之前。

另一个优化,这个索引里的每条数据都是一个非空的accout_id字段,accout_id在query语句里会用于terms查询。遂把这个accout_id字段作为routing进行存储。同时可以对查询语句进行修改:

#原query
"filter": [
    {
        "terms": {
            "account_id": [
                "abc123"
            ],
            "boost": 1
        }
    }
...
]
#改为
"filter": [
    {
        "terms": {
            "_routing": [
                "abc123"
            ]
        }
    }
...
]

查询改为_routing之后,发现整体的search latency大幅降低。

经过这两次改动,针对这个索引的search latency基本满足需求。

另外,还有一个小改动,通过preload docvalue, 可以减少首次查询的耗时。

原文地址:https://www.cnblogs.com/zhq1007/p/11689361.html

时间: 2024-08-30 10:45:12

Elasticsearch搜索调优的相关文章

ElasticSearch的部署、同步与调优

ElasticSearch是一个强大的搜索服务器,基于Apache Lucene的全文搜索引擎开发,具有高性能.分布式和零配置的优点.在当前的项目中,我们希望ES能承担亿级文档的搜索,而ES也证明了即便面对这样的数据规模,也能实现十分迅速的搜索响应. 概念 节点(Node):节点是一个ES的实例,一般一台主机上部署一个节点- 集群(Cluster):集群由若干节点组成,和任意节点的通信等价于和集群的通信 分片(Shard):一个索引会分成多个分片存储,分片数量在索引建立后不可更改 副本(Repl

Elasticsearch 基础理论 & 配置调优

一.简介 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎. 它不但包括了全文搜索功能,还可以进行以下工作: 分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索. 实时分析的分布式搜索引擎. 可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据. 使用案例: 维基百科使用Ela

Elasticsearch 调优 (官方文档How To)

How To Elasticsearch默认是提供了一个非常简单的即开即用体验.用户无需修改什么配置就可以直接使用全文检索.结果高亮.聚合.索引功能. 但是想在项目中使用高性能的Elasticsearch,有几方面优化方法最好掌握. 本文就是为了引导如何优化. 常规建议 不要一次返回太大量的搜索结果集 Elasticsearch设计作为一个搜索引擎,非常擅长返回匹配的查询结果.但是,它并不合适像数据库一样,把整个document作为查询结果返回.如果非要这样做,最好还是使用Scroll这个接口来

[数据库]漫谈ElasticSearch关于ES性能调优几件必须知道的事(转)

ElasticSearch是现在技术前沿的大数据引擎,常见的组合有ES+Logstash+Kibana作为一套成熟的日志系统,其中Logstash是ETL工具,Kibana是数据分析展示平台.ES让人惊艳的是他强大的搜索相关能力和灾备策略,ES开放了一些接口供开发者研发自己的插件,ES结合中文分词的插件会给ES的搜索和分析起到很大的推动作用.ElasticSearch是使用开源全文检索库ApacheLucene进行索引和搜索的,说架构必须和Lucene的一些东西打交道. 关于Lucene: Ap

漫谈ElasticSearch关于ES性能调优几件必须知道的事(转)

ElasticSearch是现在技术前沿的大数据引擎,常见的组合有ES+Logstash+Kibana作为一套成熟的日志系统,其中Logstash是ETL工具,Kibana是数据分析展示平台.ES让人惊艳的是他强大的搜索相关能力和灾备策略,ES开放了一些接口供开发者研发自己的插件,ES结合中文分词的插件会给ES的搜索和分析起到很大的推动作用.ElasticSearch是使用开源全文检索库ApacheLucene进行索引和搜索的,说架构必须和Lucene的一些东西打交道. 关于Lucene: Ap

别再说你不会ElasticSearch调优了,都给你整理好了

ES 发布时带有的默认值,可为 ES 的开箱即用带来很好的体验.全文搜索.高亮.聚合.索引文档 等功能无需用户修改即可使用,当你更清楚的知道你想如何使用 ES 后,你可以作很多的优化以提高你的用例的性能,下面的内容告诉你 你应该/不应该 修改哪些配置. 第一部分:调优索引速度 使用批量请求批量请求将产生比单文档索引请求好得多的性能. 为了知道批量请求的最佳大小,您应该在具有单个分片的单个节点上运行基准测试. 首先尝试索引100个文件,然后是200,然后是400,等等. 当索引速度开始稳定时,您知

【scikit-learn】网格搜索来进行高效的参数调优

 内容概要? 如何使用K折交叉验证来搜索最优调节参数 如何让搜索参数的流程更加高效 如何一次性的搜索多个调节参数 在进行真正的预测之前,如何对调节参数进行处理 如何削减该过程的计算代价 1. K折交叉验证回顾? 交叉验证的过程 选择K的值(一般是10),将数据集分成K等份 使用其中的K-1份数据作为训练数据,另外一份数据作为测试数据,进行模型的训练 使用一种度量测度来衡量模型的预测性能 交叉验证的优点 交叉验证通过降低模型在一次数据分割中性能表现上的方差来保证模型性能的稳定性 交叉验证可以用

ElasticSearch中的JVM性能调优

ElasticSearch中的JVM性能调优 前一段时间被人问了个问题:在使用ES的过程中有没有做过什么JVM调优措施? 在我搭建ES集群过程中,参照important-settings官方文档来的,并没有对JVM参数做过多的调整.其实谈JVM配置参数,少不了操作系统层面上的一些配置参数,比如 page cache.同时ES进程需要打开大量文件,文件描述符的个数(/etc/security/limits.conf)也要配置得大一些.这里简要介绍一下关于ES JVM参数的配置: 将 xms 和 x

39套精品Java从入门到架构师|高并发|高性能|高可用|分布式|集群|电商缓存|性能调优|设计项目实战|视频教程

精品Java高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,大型分布式电商项目实战视频教程   视频课程包含: 39套Java精品高级课架构课包含:java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,架构设计,web安全,高并发,高性能,高可用,高可扩展,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,工作流,程序调优,负载均衡,Solr