参考:https://www.phpmianshi.com/?id=216
常见优化策略:
filter过滤器查询优化
结果分数是Elasticsearch的关键。 通常,当您使用搜索引擎时,您需要最准确的结果。 例如,如果您正在搜索“苹果”,您不希望结果包括“苹果手机”。
Elasticsearch根据您提供的参数对查询结果进行评分。
虽然查询相关性不是本篇文章的重点,但重要的是在此提及,因为如果您有快速搜索需求但结果不是您要查找的结果,则整个搜索都是浪费时间。 那么,你如何加快搜索速度?
1 查询时,使用query-bool-filter组合取代普通query
提高搜索性能的一种方法是使用过滤器。 过滤后的查询可能是您最需要的。
首先过滤是很重要的,因为搜索中的过滤器不会影响文档分数的结果,因此您在资源方面使用很少的资源来将搜索结果范围缩小到很小。
使用过滤查询,结合使用布尔匹配,您可以在评分之前搜索包含X的所有文档,或者不包含Y的所有文档。此外,可以filter是可以被缓存的。
2 避免使用script查询
避免使用脚本查询来计算匹配。 推荐:建立索引时存储计算字段。
例如,我们有一个包含大量用户信息的索引,我们需要查询编号以“1234”开头的所有用户。 您可能希望运行类似“source”的脚本查询: doc ['num'].value.startsWith('1234') 此查询非常耗费资源并且会降低整个系统的速度。 合理的建议:考虑在索引时添加名为“num_prefix”的字段。 然后我们可以查询 “name_prefix”:“1234”。
3 避免使用wildcard查询
主要原因:wildcard类似mysql中的like,和分词完全没有了关系。
出现错误:用户输入的字符串长度没有做限制,导致首尾通配符中间可能是很长的一个字符串。 后果就是对应的wildcard Query执行非常慢,非常消耗CPU。
根本原因:为了加速通配符和正则表达式的匹配速度,Lucene4.0开始会将输入的字符串模式构建成一个DFA (Deterministic Finite Automaton),带有通配符的pattern构造出来的DFA可能会很复杂,开销很大。
可能的优化方案:
-
wildcard query应杜绝使用通配符打头,实在不得已要这么做,就一定需要限制用户输入的字符串长度。
-
最好换一种实现方式,通过在index time做文章,选用合适的分词器,比如nGram tokenizer预处理数据,然后使用更廉价的term query来实现同等的模糊搜索功能。
-
对于部分输入即提示的应用场景,可以考虑优先使用completion suggester, phrase/term/suggeter一类性能更好,模糊程度略差的方式查询,待suggester没有匹配结果的时候,再fall back到更模糊但性能较差的wildcard, regex, fuzzy一类的查询。
详尽原理参考:https://elasticsearch.cn/article/171
4 合理使用keyword类型
ES5.x里对数值型字段做TermQuery可能会很慢。
在ES5.x+里,一定要注意数值类型是否需要做范围查询,看似数值,但其实只用于Term或者Terms这类精确匹配的,应该定义为keyword类型。
典型的例子就是索引web日志时常见的HTTP Status code。
详尽原理参考:https://elasticsearch.cn/article/446
5 控制字段的返回
一是:数据建模规划的时候,在Mapping节点对于仅存储、是否构建倒排索引通过enabled、index参数进行优化。
二是:_source控制返回,不必要的字段不需要返回,举例:采集的原文章详情内容页,根据需要决定是否返回。
6 空值的处理
如果同步的内容有可能有空值尽量设置一个默认值
6.1 比如:state状态为空为正常状态,则可以设置一个默认none,查询正常状态就可以term=none 查询不为空可以把所有可能的情况都列出来用terms
SELECT (CASE state WHEN '' THEN 'none' ELSE state END) as es_state from table
6.2 当然最好还是用 keyword分析器来查询, 比如 filed.keyword="" 这样也是能正常查询出来的
常见问题和解决方案
1.text类型需要排序
报错如下:
Fielddata is disabled on text fields by default. Set fielddata=true on [gender] in order to load fielddata in memory by uninverting the inverted index
场景:
定义了is_hot 是否热门,为了让它支持精确查询,定义成了text类型,但是还需要先根据是否热门排序,再根据热度排序
问题原因:
根据官方文档显示,出现该错误是因为5.x之后,Elasticsearch对排序、聚合所依据的字段用单独的数据结构(fielddata)缓存到内存里了,但是在text字段上默认是禁用的,如果有需要单独开启,这样做的目的是为了节省内存空间。
官方文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata.html
开启方法:
curl -XPUT 'http://localhost:9200/my_index/_mapping' -d ' { "properties": { "is_hot": { "type": "text", "fielddata": true } } }'