elasticsearch源码分析之search模块(server端)
elasticsearch源码分析之search模块(server端)
继续接着上一篇的来说啊,当client端将search的请求发送到某一个node之后,剩下的事情就是server端来处理了,具体包括哪些步骤呢?
过程
一、首先我们来看看接收地方其实就是在org.elasticsearch.action.search.TransportSearchAction
中,收到请求之后会判断请求的index的shard是否只有一个,如果是一个的话,那么会强制将请求的type设置为QUERY_AND_FETCH
,因为所以的事情在此shard上就能够做完了。所以如果设置了routing,而让请求落在了一个shard上时,搜索的效率会高很多的原因。
二、根据不同的type来确定不同的处理方式,这里补充一下,上一篇可能忘记说了,search的type一般来说分为“DFS_QUERY_THEN_FETCH、QUERY_THEN_FETCH、DFS_QUERY_AND_FETCH、QUERY_AND_FETCH”这四种,还有“SCAN、COUNT”在ES2.X里面其实已经被舍弃掉了。我们一般都是用的默认的QUERY_THEN_FETCH,上面说的一个shard的除外。所以本篇就只讨论这种情况了。
三、得到搜索的index所涉及的shard,并依次执行: 1、获取该shard所在的node并执行sendExecuteFirstPhase
,实际上是向node发送了一个“QUERY”的请求:
transportService.sendRequest(node, QUERY_ACTION_NAME, request, new ActionListenerResponseHandler<QuerySearchResultProvider>(listener) {
@Override
public QuerySearchResult newInstance() {
return new QuerySearchResult();
}
});
2、node接收到"QUERY"的请求之后,执行executeQueryPhase
:首先是创建一个search的context,
SearchContext context = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher, defaultSearchTimeout);
创建的具体过程就不详细说了,之后做的事情还是有parseSource、对size做判断(2.X里面最大不超过10000,可以通过配置文件配置)、……
最重要的其实是loadOrExecuteQueryPhase(request, context, queryPhase);
,具体的内容是首先从cache里面执行query,如果cache里面没有找到,才会执行queryPhase:queryPhase.execute(context);
;里面的处理逻辑就比较复杂了,但是最重要的是searcher.search(query, collector);
,其实是调用了Lucene里面IndexSeartcher
的search方法。
3、如此一来,第一阶段的query已经做完了,,接下来便是fetch的执行,入口在onFirstPhaseResult
这里,在底层同样是向node发送一个“FETCH”请求咯:
4、node接收到“fetch”请求之后,执行executeFetchPhase
:
fetch的核心代码如下:
。。。
大意就是轮流通过之前query结果中的docid,然后创建出InternalSearchHit
的集合,并将之放在fetchResult中context.fetchResult().hits(new InternalSearchHits(hits, context.queryResult().topDocs().totalHits, context.queryResult().topDocs().getMaxScore()));
,并将之返回到发送fetch的node。
四、到目前为止,该获取的数据都已经拿到了,现在要做的则是要把个node的返回结果做merge,merge的操作由SearchPhaseController
来控制:
final InternalSearchResponse internalResponse = searchPhaseController.merge(sortedShardList, firstResults,
fetchResults, request);
具体的过程就不细说了,大体就是该排序的就做排序,有aggs的就做aggs……
五、通过listener将上面的结果返回:listener.onResponse(new SearchResponse(internalResponse, scrollId, expectedSuccessfulOps, successfulOps.get(), buildTookInMillis(), buildShardFailures()));
给发出接收search请求的node,也就是上一篇说道的client。
总结
这样知道了,为什么返回的结果长那么个鬼样子了。整个过程的话算是走马观花地走了一遍了,其实里面还有很多detail的东西没用讲到,看一张图就知道了:
包括他们分别的具体实现什么的,所以一个查询牵扯到的东西实在太多,等有时间再去深究,可都是财富。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」