33

ES 查询性能调优实践,亿级数据查询毫秒级返回

 5 years ago
source link: https://www.tuicool.com/articles/ziQr63e
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1、概述

本文简要描述ES查询性能的优化过程。忽略很多细节,其实整个过程并不顺利,因为并没有一个明确的指引,教你怎么做就能让性能大幅提升。很多时候不同业务有不同的场景,还是需要自己摸索一番。比如用filter过滤取代query查询,明明官方文档说filter速度更快。但应用到我们业务来,一开始却没有明显效果。经过反复测试,发现虽然filter可以省略计算分数的环节,但我们的业务查询场景,一次返回数据量不会很多,最大的瓶颈不在于打分,而在于range过滤和排序。可是range过滤和排序,这方面在网上却很少被提及。所以还是要自己根据业务场景多思考多验证。
一开始优化效果很突出,耗时从100秒一下子降低到10秒以内。但越往后其实越难做。好比从10分考到60分不容易,但90分考到95分就更加困难。
我们自己定的优化目标是1秒,是达到效果了。

2、ES查询性能优化效果

优化前,随便一个ES查询耗时就高达170秒,接近3分钟。

rUbqUzj.jpg!web

注意上面的ES查询语句还不是最复杂的,还可以加上qua搜索、关键词模糊搜索,再把日期范围拉长,再加上日期排序。无法想像,耗时会变得多么惨不忍睹,简直要变成离线查询了。

nQVvAri.jpg!web

可实际上,yiya页面是在线用户进行实时查询,所以这么高的耗时是不能忍受的,需要优化。那么,数据量有上亿,耗时优化到多少才合适呢?作为一个实时接口,能不能做到1秒内ES查询返回结果呢?

经过优化,ES查询耗时从之前的动辄3分钟,已经变成毫秒级了。

ZRviume.jpg!web

3、ES查询性能调优

3.1 拆分索引

ES能存千亿数据,不表示你可以在匹配到千亿数据时还能秒级返回。拆分索引是指你在搜索时,必须尽量缩小搜索的数据集范围。

  1. 按照数据源拆分,每个数据源独立索引。

  2. 按照时间拆分,每月建索引。

原先是按照数据源天然地分开索引。但日积月累,单个数据源的数据也日益膨胀,月新增一亿条数据。所以要按照时间拆分,把单个数据源按照年月进一步地拆分。

3.2 字段拉平

原先是把几个搜索字段都放在extra_info里面,导致只能在extra_info进行搜索。这带来两个问题:

  1. extra_info字段巨大,查询性能很低。

  2. 由于extra_info字段是黑盒,只能在里面进行模糊匹配,性能进一步降低。

Qzy6JnV.jpg!web

优化后,把搜索字段全部拉平到上一层,不再从extra_info字段进行查询。

6BJN3yu.jpg!web

3.3 减少模糊匹配

模糊匹配耗时会随数据量线性增长,尽量使用match匹配(有索引),避免使用模糊匹配(wildcard)。
跟yiya业务方沟通过,qua字段的搜索没有必要是模糊搜索,所以修改为精确匹配。
但业务方要求关键词搜索必须是模糊搜索。不过即使模糊匹配,也尽量避免左模糊这样的模式匹配,资源消耗严重。可以使用match进行分词搜索。

3.4 使用日期字段搜索范围

原先ES的日期date_created字段是用字符串存储。

jIZnAbJ.png!web

但对字符串的字段类型进行range过滤并不高效。
字符串范围适用于一个基数较小的字段,一个唯一短语个数较少的字段。你的唯一短语数越多,搜索就越慢。
数字和日期字段的索引方式让他们在计算范围时十分高效。但对于字符串来说却不是这样。为了在字符串上执行范围操作,Elasticsearch 会在这个范围内的每个短语执行 term 操作。这比日期或数字的范围操作慢得多。
优化后,date_created字段改成日期类型。

uaYJnib.png!web

3.5 使用过滤器上下文

ERvqye6.jpg!web

原先使用的是query查询子句,优化后改成filter过滤器。
query查询子句用于回答“这个文档与此子句相匹配的程度”,而filter过滤器子句用于回答“这个文档是否匹配这个子句”,Elasticsearch只需要回答“是”或“否”,不需要为过滤器子句计算相关性分数,而且过滤器结果可以缓存。
查询在Query查询上下文和Filter过滤器上下文中,执行的操作是不一样的:

  1. 查询上下文:
    在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的相关度高么?”
    如何验证匹配很好理解,如何计算相关度呢?ES中索引的数据都会存储一个_score分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的,因此也需要一定的时间。
    查询上下文 是在 使用query进行查询时的执行环境,比如使用search的时候。

  2. 过滤器上下文:
    在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?”
    答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。
    过滤上下文 是在使用filter参数时候的执行环境,比如在bool查询中使用Must_not或者filter。
    另外,过滤器上下文中,查询的结果可以被缓存。

filter速度要快于query,filter是不计算相关性的,同时可以cache。所以尽可能使用过滤器上下文(Filter)替代查询上下文(Query)。
因为业务场景并不需要计算相关性分数,所以改用filter。

eInI7vQ.jpg!web

后期我们会根据每个维度陆续写相关的测试文章,如果你有兴趣,请关注我们哦。

长按指纹识别图中的二维码,获取更多测试干货分享!

mmyqMzA.gif
iIZ7baZ.jpg!web

YFn2e2F.gif 将我们公众号置顶   不会漏掉我们的原创干货哦!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK