Elasticsearch的搜索会分两个阶段:Query 和 Fetch
1 Query-then-Fetch
query
coordinating node接收到请求
选择主分片(若有副本,则会在主分片及其对应副本中随机选择一台)发送查询请求
被选中的分片执行查询,进行排序。
分片返回From+size个排序后的文档id和排序值给Coordinating节点
fetch
coordinating node将query阶段获取到的文档id进行重新排序,选取From+Size个文档的id
以multi get请求的方式,到相应的分片获取详细的文档数据
潜在问题
性能问题(深度分页)
每个分片需要查询的文档个数=from+size
协调节点需要处理:number_of_shard * (from + size)
相关性算分
每个分页都基于自己的分片上的数据进行相关度的计算。这会导致打分偏离的清空,特别是数据量很少时。相关性算分在分片之间是相互独立。当文档总数很少的情况下,如果主分片大于1,主分片越多结果越不准
2 解决算分不准的方法
数据量方面
数据量不大的情况下,将主分片数设置为1
数据量大的时候,确保文档均匀分散在各个分片上,主要确保hash(_routing)均匀分布
使用DFS Query Then Fetch
搜索的URL中指定参数“_search?search_type=dfs_query_then_fetch”
到每个分片把各分片的词频和文档频率进行搜集,然后完整的进行一次相关性算分,耗费更加多的CPU和内存,执行性能低下,一般不建议使用
3 深度分页
限制最大查询的文档数量
通过 index.max_result_window设置,默认10000
通过Search After查询
不支持指定页数(from),只能往下翻
第一步搜索需要指定sort,并且保证值是唯一的(可以通过加入_id保证唯一性)
使用上一次,最后一个文档的sort值进行查询
通过唯一排序值定位,将每次要处理的文档数都控制在size,而不是之前的from+size个
Scroll API
创建一个快照,有新的数据写入以后,无法被查询
每次查询后,输入上一次的scroll id
适用于大数据量分页,不支持向前
场景比较
from 和 size
需要实时获取顶部的部分文档,例如查询最新的文档
Scroll API
不支持向前翻页
如整点数据全量导出
对某个时间节点的数据进行操作
不需要排序的大数据量查询,翻页
Search After
不支持向前翻页
需要指定唯一排序字段
需要排序的大数据量的查询,翻页
3.1 Search After
POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":11}
POST users/_doc
{"name":"user2","age":12}
POST users/_doc
{"name":"user2","age":13}
POST users/_count
POST users/_search
{
"size": 1,
"query": {
"match_all": {}
},
"sort": [
{"age": "desc"} ,
{"_id": "asc"}
]
}
POST users/_search
{
"size": 1,
"query": {
"match_all": {}
},
"search_after":
[
13,
"G6w-sXEBuoXto2dXTeXA"],
"sort": [
{"age": "desc"} ,
{"_id": "asc"}
]
}
#其中search_after参数来自于上一次查询返回值的sort参数复制
3.2 Scroll API
POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":20}
POST users/_doc
{"name":"user3","age":30}
POST users/_doc
{"name":"user4","age":40}
POST /users/_search?scroll=5m
{
"size": 1,
"query": {
"match_all" : {
}
}
}
#设置5分钟的快照,每次获取一条记录
POST users/_doc
{"name":"user5","age":50}
#快照创建完之后新增记录,用于验证快照内容的范围
POST /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAWAWbWdoQXR2d3ZUd2kzSThwVTh4bVE0QQ=="
}
#scroll_id来源于创建快照时的返回值复制