Search执行时分为两个步骤
Query阶段
Fetch阶段
合并起来称为query-then-fetch
相关性算分问题
相关性算分在shard与shard之间时相互独立的,也就意味着同一个Term的IDF等值在不同的shard上时不同的,文档的相关性算分和它所处的shard相关。
在文档数量不多时,会导致相关性算分严重不准的情况发生。
解决思路有两个:
一是设置分片数为1,从根本上排除问题,在文档数量不多的时候可以考虑该方案,比如百万到千万级别的文档数量。
二是使用DFS query-then-fetch查询方式。
DFS query-then-fetch是在拿到所有文档后再进行完整的计算一次相关性算分,耗费更过的cpu和内存,执行性能也比较低下,一般不建议使用。使用方式如下:
排序
es默认会采用相关性算分进行排序,用户可以通过指定sort参数来自行设定排序规则
按照字符串排序比较特殊,因为es有text和keyword两种类型,针对text类型进行排序,如下所示:
针对keyword类型进行排序,可以返回预期结果
排序过程的实质是对字段的原始内容进行排序,这个过程中倒排索引无法发挥作用,需要用到正排索引。
es对此提供了两种方式实现:
fielddata,默认禁用。
doc values,默认启用,除text类型。
fielddata 和 doc values的区别
fielddata是默认关闭的,指针对text类型有效果,可以通过如下API进行开启:
此时字符串是按照分词后的term进行排序,往往结果很难复合预期。
一般是对分词进行聚合分析时开启。
doc values默认是启用的,可以在创建索引的时候关闭
如果后面要再开启doc values,需要做reindex操作。
docvalue_fields
可以通过该字段获取fielddata和doc values中存储的内容。
分页与遍历
es提供了3种方式来解决分页与遍历的问题:
from/size
scroll
search_after
from/size
最常用的分页方案
from指明开始位置。
size指明获取总数。
深度分页是一个经典问题,在数据分片的情况下如何获取前1000个文档?
scroll
遍历文档集的api,以快照的方式来避免深度分页的问题
不能用来做实时搜索,因为数据不是实时的。
尽量不要用复杂的sort条件,使用_doc最高效。
使用稍显复杂。
第一步需要发起1个scroll search,如下所示:
es在收到该请求后会根据查询条件创建符合条件的文档Id集合快照。
第二步调用scroll search API,获取文档集合,如下所示:
不断迭代调用直到返回的hits.hits为空为止。
过多的scroll调用会占用大量的内存,可以通过clear api删除过多的scroll快照:
Search After
避免深度分页的性能问题,提供实时的下一页文档获取功能。
缺点不能使用from参数,即不能指定页码。
只能下一页,不能上一页。
使用简单。
第一步为正常的搜索,但要指定sort值,并保证唯一性。
第二部为使用上一步返回的sort值进行查询。
如何避免的深度分页问题
三种分页的应用场景