elasticsearch搜索过程分析

(一)通过HTTP请求调用搜索服务
示例:

GET http://localhost:9200/index_test/_search
{
   "query": {
       "query_string": {
          "default_field": "title",
          "query": "this is title field"
       }
   }
}

(二)相关说明
1、搜索类型
query_then_fetch/dfs_query_then_fetch:默认是query_then_fetch搜索,现在5.3.1版本只有这两种搜索类型(SearchType枚举类)
query_and_fetch/dfs_query_and_fetch:内部搜索优化,不支持REST调用,现已移除
2、主要协议
indices:admin/shards/search_shards
indices:data/read/search
indices:data/read/search[phase/query]:query阶段
indices:data/read/search[phase/fetch/id]:fetch阶段
3、主要服务类
TransportSearchAction
SearchQueryThenFetchAsyncAction
SearchDfsQueryThenFetchAsyncAction
SearchTransportService
SearchPhaseController
SearchService
QueryPhase
FetchPhase
OperationRouting
(三)具体搜索的过程
  1、HTTP到TCP的调用链:
RestController -> RestSearchAction -> TransportClient -> TransportSearchAction.doExecute(task,request,listener);
  searchRequestBuilder.execute()调用链:
TransportClient -> TransportSearchAction.doExecute(task,request,listener)
  2、 如果有跨集群搜索,按照不同的集群将请求中的索引分组,然后通过RemoteClusterService的collectSearchShards方法针对每个集群将搜索请求发送出去,目标集群TransportSearchAction收到请求调用doExecute方法处理。本示例只搜索一个集群中的一个索引,没有分组,调用executeSearch方法直接处理
  3、解析索引别名关联的过滤器;获取请求中的routing路由参数信息;
  4、OperationRouting计算目标Shard列表
   4.1、首先从ClusterState集群路由表IndexRoutingTable中获取所有IndexShardRoutingTable,每个IndexShardRoutingTable存有一个分片所有相关信息,包括其主分片和副本的,运行中的和未分配的等等(本示例是1个主分片4个副本,所以只有一个IndexShardRoutingTable)
   4.2、遍历IndexShardRoutingTable列表,计算IndexShardRoutingTable中该次请求应该访问的具体Shard
    4.3、查看本次请求是否有preference参数,可根据用户参数请求只搜索主分片、只搜索副本、搜索特定Shard、搜索指定Node等,有的话根据preference参数选择特定的节点和分片:
    4.4、没有指定preference参数,根据IndexShardRoutingTable路由表处理:优先选择运行中的Shard,其次是迁移中的Shard,通过AtomicInteger维护一个计数器,每次加1,对Shard列表取余,本质是一个轮询的算法,需要查询特定的Shard列表封装到GroupShardsIterator迭代器中,后面会检查查询的Shard数量是否超过限制,过多的Shard查询非常影响性能,所以默认上限1000个
  5、解析索引的boost加权参数;如果只查询一个Shard或者只进行搜索建议查询,将搜索类型置为query_then_fetch,因为一个Shard没有必要再走dfs_query_then_fetch
  6、根据搜索类型的不同构造不同的action实例,query_then_fetch为SearchQueryThenFetchAsyncAction,dfs_query_then_fetch为SearchDfsQueryThenFetchAsyncAction,处于效率性能等方面的考虑,一般用query_then_fetch查询
  7、SearchQueryThenFetchAsyncAction调用start方法处理,迭代GroupShardsIterator,针对每个Shard,调用performInitialPhase方法开始第一阶段的搜索处理,与目标Shard的Node建立连接,通过SearchTransportService的sendExecuteQuery将请求(indices:data/read/search[phase/query])发送出去,如果请求的Shard只有一个,该阶段就获取Document文档,通过QueryFetchSearchResult保存搜索结果,其他情况不会自动获取Document,通过QuerySearchResult保存搜索结果;请求处理类在SearchTransportService类中注册,为TaskAwareTransportRequestHandler类,TaskAwareTransportRequestHandler收到请求后调用SearchService的executeQueryPhase方法处理第一阶段的query
  8、为ShardSearchTransportRequest请求创建DefaultSearchContext上下文对象,DefaultSearchContext存有请求参数、搜索结果、Shard信息、统计等等跟本次搜索相关的所有信息;SearchService调用loadOrExecuteQueryPhase方法,首先判断该次请求是否可被缓存(根据请求参数、es配置、query_then_fetch搜索类型、size是否为0等判断),可以的话就从缓存中加载数据,调用IndicesService的loadIntoContext方法将数据加载到context上下文对象中,没有缓存数据调用QueryPhase的execute(searchContext)方法走正常搜索流程;主要分为查询、重新打分、搜索提示、聚合四个子流程
   8.1、查询
  确定获取的文档数,为总文档数和from+size的较小值,例如每页10条,检索第2页的数据,需要查询出20条,排序将10-19编号返回
  确定Lucene Collector文档收集器对象,Collector负责收集命中的TopDocs文档,可以定制不同的Collector进行个性化的排序、过滤等,根据请求的不同使用不同的Collector类型,比如TopDocsCollector( TopDocs文档收集器的基类,通过PriorityQueue优先级队列存储top n数据)、TopScoreDocCollector(ScoreDoc文档收集器的基类,ScoreDoc是TopDocs中的一条数据,默认按score降序排列,score相同则按id升序排列)、TotalHitCountCollector(只获取相关文档数时用)、ProfileCollector(统计时间开销,类似Java的IO流类,可以装饰别的Collector)、TimeLimitingCollector(根据设置的阈值及时停止耗时的操作,默认配置search.default_search_timeout为-1,即不超时,同样可装饰别的Collector),CancellableCollector(elasticsearch定制,可取消查询)等,本示例是SimpleTopScoreDocCollector(TopScoreDocCollector的实例);如果需要获取执行时间统计信息,用InternalProfileCollector包装SimpleTopScoreDocCollector;让查询可被取消用CancellableCollector进行包装
  在构造Collector的过程中,主要涉及scroll滚动、collapse字段折叠、post filter过滤器、超时collector等逻辑处理
调用IndexSearcher.search(query, collector)交给Lucene进行查询,随后从collector中得到TopDocs数据,放到QuerySearchResult或QueryFetchSearchResult中
接下来如果需要还要进行重新打分,搜索提示和聚合操作的处理
   8.2、重新打分
   8.3、搜索提示
   8.4、聚合
  9、每次查询执行成功后,AtomicInteger计数器加1,所有Shard查询请求处理完成之后:如果只牵扯到单个Shard,直接调用SearchService的executeFetchPhase进行处理,首先确定需要加载文档数据的doc id,区间为[from,from+size),放到context中;
其它情况Listener回调触发SearchQueryThenFetchAsyncAction内部类FetchPhase的run方法:
  SearchPhaseController对获取到的ScoreDoc数组进行排序,取得特定区间的ScoreDoc([from,from+size))
  对ScoreDoc按Shard分组,hppc类库IntArrayList[]存储结构,IntArrayList存有一个Shard到一个或多个docId的映射
  针对每个Shard发送fetch请求,每次返回结果收集到CountedCollector中,全部结果返回后调用sendResponseAsync方法异步执行合并操作得到InternalSearchResponse并给用户返回结果
  合并多个fetch数据主要是SearchPhaseController的merge方法,主要合并搜索提示、聚合、统计等信息
  10、fetch阶段主处理流程为FetchPhase的execute(context)方法
检查context中的StoredFieldsContext(存有配置store=yes的field字段信息)、ScriptFieldsContext(利用painless,groovy等脚本处理的script_fields字段)、FetchSourceContext(如果没有前两个context,默认加载source字段数据),FieldsVisitor存有需要加载的store字段信息,内置的默认加载字段主要有_routing、_ttl、_parent、_timestamp、_source、_uid
  确定需要加载的字段后,对于每一个docId,加载字节数据到FieldVisitor中,context的SearchLookup解析得到所有字段的数据,得到了InternalSearchHit对象,但是现在很多字段数据都没有填充,只有id,type等信息
  接下来是9个子阶段的处理,下面都是FetchSubPhase接口的实现类,每个InternalSearchHit都会被这些FetchSubPhase处理一遍,每个实现类负责不同的数据组装

ExplainFetchSubPhase :打分解释说明
DocValueFieldsFetchSubPhase :doc value字段的数据,比如 "docvalue_fields": ["field1", "field2"]
ScriptFieldsFetchSubPhase :script字段数据
FetchSourceSubPhase :从上述SourceLookup中获取source字段数据填充到hit中
VersionFetchSubPhase :文档的version信息
InnerHitsFetchSubPhase:inner hits 数据填充
HighlightPhase :高亮信息
PercolatorHighlightSubFetchPhase:继承HighlightPhase ,根据文档得到符合要求的查询为Percolator查询(正常查询的逆过程),高亮单独处理
ParentFieldSubFetchPhase :父子文档中父文档信息

  最后InternalSearchHit[]数组对象还有2个子阶段的处理

PercolatorHighlightSubFetchPhase :为每个InternalSearchHit填充高亮信息MatchedQueriesFetchSubPhase :为每个InternalSearchHit获取匹配到的查询名称

  11、query和fetch处理完成,返回相关文档数据

  12、文档资料

elastic:https://www.elastic.co/guide/index.html
percolate query:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-percolate-query.html#query-dsl-percolate-query
inner hits:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-inner-hits.html#search-request-inner-hits
doc values:https://www.elastic.co/guide/en/elasticsearch/reference/current/doc-values.html
profile api:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-profile.html
field collapsing:https://www.elastic.co/guide/en/elasticsearch/guide/current/top-hits.html

猜你喜欢

转载自blog.csdn.net/ok0011/article/details/82184872