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的東西沒用講到,看一張圖就知道了:
包括他們分別的具體實現什么的,所以一個查詢牽扯到的東西實在太多,等有時間再去深究,可都是財富。