最近把搜索后端从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进行有序存储,方便单segment内的查询提前中断查询,提升查询效率。

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, 可以减少首次查询的耗时。

Elasticsearch搜索调优的更多相关文章

  1. Elasticsearch搜索调优权威指南 (2/3)

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/AAkVdzmkgdBisuQZldsnvg 英文原文:https://qbox.io/blog/el ...

  2. Elasticsearch搜索调优权威指南 (1/3)

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/qwkZKLb_ghmlwrqMkqlb7Q英文原文:https://qbox.io/blog/ela ...

  3. elasticsearch性能调优

    转载 http://www.cnblogs.com/hseagle/p/6015245.html 该es调优版本可能有低,但是思想主体不变,不合适的参数可以自己找最新的版本相应的替代,或者增删 ela ...

  4. elasticsearch 性能调优

    所有的修改都可以在elasticsearch.yml里面修改,也可以通过api来修改.推荐用api比较灵活 1.不同分片之间的数据同步是一个很大的花费,默认是1s同步,如果我们不要求实时性,我们可以执 ...

  5. 腾讯健康码16亿亮码背后的Elasticsearch系统调优实践【>>戳文章免费体验Elasticsearch服务30天】

    [活动]Elasticsearch Service免费体验馆>>Elasticsearch Service新用户特惠狂欢低至4折>>Elasticsearch Service企 ...

  6. filebeat的层次架构图和配置部署 -- 不错的文档 - elasticsearch 性能调优 + Filebeat配置

    1.fielbeat的组件架构-看出层次感 2.工作流程:每个harvester读取新的内容一个日志文件,新的日志数据发送到spooler(后台处理程序),它汇集的事件和聚合数据发送到你已经配置了Fi ...

  7. elasticsearch运维实战之2 - 系统性能调优

    elasticsearch性能调优 集群规划 独立的master节点,不存储数据, 数量不少于2 数据节点(Data Node) 查询节点(Query Node),起到负载均衡的作用 Linux系统参 ...

  8. ELASTIC SEARCH 性能调优

    ELASTICSEARCH 性能调优建议 创建索引调优 1.在创建索引的使用使用批量的方式导入到ES. 2.使用多线程的方式导入数据库. 3.增加默认刷新时间. 默认的刷新时间是1秒钟,这样会产生太多 ...

  9. 一次看完28个关于ES的性能调优技巧,很赞,值得收藏!

    因为总是看到很多同学在说Elasticsearch性能不够好.集群不够稳定,询问关于Elasticsearch的调优,但是每次都是一个个点的单独讲,很多时候都是case by case的解答,本文简单 ...

随机推荐

  1. GIAC2019 演讲精选 | 面向未来的黑科技——UI2CODE闲鱼基于图片生成跨端代码

    一直以来, 如何从‘视觉稿’精确的还原出 对应的UI侧代码 一直是端侧开发同学工作里消耗比较大的部分,一方面这部分的工作 比较确定缺少技术深度,另一方面视觉设计师也需要投入大量的走查时间,有大量无谓的 ...

  2. nodeJs学习-01 http模块

    http模块基础: const http = require("http"); //引入http系统模块 var server = http.createServer(functi ...

  3. pycharm 快捷键使用

    1.Ctrl+/?键 = 选中行全部注释/解封: 2.Ctrl+D = 复制前一行: 3.Ctrl+Z = 撤销: 1.编辑(Editing) Ctrl + Space 基本的代码完成(类.方法.属性 ...

  4. SDUT-2118_数据结构实验之链表三:链表的逆置

    数据结构实验之链表三:链表的逆置 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 输入多个整数,以-1作为结束标志,顺序 ...

  5. notepad++最有用的快捷键

    Ctrl+Tab  实现在多个打开的窗口间切换 Ctrl+Shift+Q 区块注释 Ctrl+K  行注释 Tab 缩进 Shift+Tab 删除缩进 先按住键盘上的“ctrl”键不放,然后滚动鼠标的 ...

  6. webpack详述

    一.利用package.json执行打包任务 首先使用npm init生成package.json文件:然后配置scripts如下: "scripts": { "buil ...

  7. HZOJ 方程的解

    乍一看还以为是道水题,没想到这玩意这么难搞. 看题显然是exgcd,然而exgcd求的是一个解而不是解的个数(考试的时候不记得通解的式子然后挂了). 对于40%的数据,直接枚举计数即可. 对于另为20 ...

  8. oracle用Where子句替换HAVING子句

    避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. 例如: 低效: ...

  9. webstorm破解教程

    1.下载地址 官网:https://www.jetbrains.com/webstorm/ 下载好之后按照提示安装即可,这里就不再多说了.下面直接说说如何使用补丁破解. 2.使用补丁破解 (http: ...

  10. HTTP协议---三次握手

    HTTP协议 关于协议 ​ 对于应用层开发人员,接触最多的网络协议通常都是传输层的TCP,为什么这么说,因为再往上的应用层协议,如:HTTP.HTTPS.POP3.SMTP.RPC.FTP.TELNE ...