1はじめに
詳述するようにはじめに、ESソースコード記述された手順を分析[ソース] Elasticsearch書き込みソースコード解析。
Elasticsearch(ES)クエリインターフェース分散データの取得、集計分析機能、データ全文検索、ログ分析と、このようなESログ解析サービスのタイプに基づいのGitHub検索プラットフォーム上のコードのような他のシーンをサポートする検索機能を有します。サポートインデックス分析、APMと、このようなシーンをモニターするなど、他のシーン、日活/リテンション分析のアプリケーションに集約分析機能。
6.7.1バージョンに基づいて、分散実行フレームワークはESおよびクエリ処理の本体を分析、ESは、分散方法クエリ、データ検索、集約能力分析を探ります。
お問い合わせの2基本的な流れ
公式サイトからの写真、バージョン6.7.1からのソースコード:
- クライアントは、受信ノードがクエリとしてコーディネーター・ノードを照会します、任意のノードにクエリを送信することができ、
- 調整ノードは、クエリ、クエリに対応するデータの断片化の分布サブタスクを解決します。
- ローカルクエリ結果の各スライスは、クライアントへのノードのバックを調整することによって、収束に、コーディネーター・ノードに返されます。
示すとおり、クライアントがノードノード3に要求を送信し、ノード3ノードクエリを解析した後、ノード1とノード2の番号0及び1、断片、にクエリが返すデータの周囲の両方の反復フラグメントに要求を分散ノード3は、ノード3は、最終的にまとめられ、その後、クライアントに返されます。
ビューの実用的な実装の観点から、上記以外コーディネータノード処理ロジックの複雑なプロセス、クエリの異なるタイプに対応するコーディネータノードの処理ロジックは、いくつかの違いを有します。
DFS_QUERY_THEN_FETCH、QUERY_AND_FETCHとQUERY_THEN_FETCH、5.3バージョンの後、QUERY_AND_FETCHが削除されました:カテゴリ2の下で一般的なクエリを導入する最初の、次の、以前のバージョンでは、クエリの3つのタイプがあります。
2.1 DFS_QUERY_THEN_FETCH
検索演算子は、論理TF(用語頻度)とDF(文書頻度)はベースポイントを算出する除算が、Elasticsearchクエリである内部、クエリは、TFの各々は、各シャードは独立しており、DFシャードであります保証ドクを_routingで配布を書いている時点であっても、独立したが、TFとDFの制服を保証することはできません、それは地元のTFとDFにつながるTFに基づいて、表示されるように、この時間を許可されていない、DFオペレータポイントは許可されていません。
この問題を解決するために、Elasticsearchは、DFS_query_then_fetchとして、DFSは問い合わせを導入し、最初の収集要求にすべてのTFとDFがシャード値、およびこれらの値は、再びquery_then_fetchを行い、ポイントのTFとDFをカウントするには、この時間が正確であるだろうA。
2.2 QUERY_THEN_FETCH
ESデフォルトのクエリ、クエリ処理、クエリ、および2つの段階に分けフェッチ:
クエリフェーズ:データの断片化およびアグリゲーションサイズ、注意戻っラウンドIDセットをスケジュールするだけで文書を検索、実際のデータを返しません。
調整ノード:解像度問合せ後、対象データの断片化に照会コマンドを送信。
ノードデータ:戻され、フィルタリング、ソート、および文書検索IDデータの集計結果の断片化と粒径のための他の条件に応じて各タイル内。
フェーズをフェッチ:重合の最終的な検索結果を生成します。
ID及び重合結果の最終セットを取得するためにクエリフェーズマージ結果を、ターゲットデータ片の送信データは、コマンド・ファイルをフェッチ:ノードを調整します。
データノード:実際に必要なクロールデータにオンデマンドコンテンツ。
3ソースクエリプロセス分析
ここでは例として、デフォルトQUERY_THEN_FETCHクエリで:
3.1クエリの入り口
これは論理であり、すべての要求を処理する類似ESあり、基準は、バルク処理を要求することができます。残りのケーススタディへのお願い:
残りの分布がRestControllerモジュールによって行われます。ときESノードが開始されると、残りのすべてのアクション要求を建て負荷、および対応するパス要求とHTTPの残りのアクションRestControllerに登録<パス、RestXXXAction>タプルとして。RestControllerモジュールのみパスのHttpよれば、このような要求に対して残り、容易アクション残りの分布に対応する要求を見つけることができます。次のようにサンプルRestSearchActionに登録しました:
public RestSearchAction(Settings settings, RestController controller) {
super(settings);
controller.registerHandler(GET, "/_search", this);
controller.registerHandler(POST, "/_search", this);
controller.registerHandler(GET, "/{index}/_search", this);
controller.registerHandler(POST, "/{index}/_search", this);
controller.registerHandler(GET, "/{index}/{type}/_search", this);
controller.registerHandler(POST, "/{index}/{type}/_search", this);
}
レイヤのHTTPリクエストパラメータを解析するために残り、RestRequest SearchRequest解析され、変換された後、次のようにSearchRequestの処理、prepareRequest方法でこのロジック、コードの一部を実行します。
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
//根据RestRequest构建SearchRequest
SearchRequest searchRequest = new SearchRequest();
IntConsumer setSize = size -> searchRequest.source().size(size);
request.withContentOrSourceParamParserOrNull(parser ->
parseSearchRequest(searchRequest, request, parser, setSize));
//处理SearchRequest
return channel -> client.search(searchRequest, new RestStatusToXContentListener<>(channel));
}
要求を処理するときNodeClient SearchRequest、要求がアクション輸送層に対応するアクションに変換され、次のようにSearchRequestは、治療作用輸送層、コード変換動作は次のとおりです。
public < Request extends ActionRequest,
Response extends ActionResponse
> Task executeLocally(GenericAction<Request, Response> action, Request request, TaskListener<Response> listener) {
return transportAction(action).execute(request, listener);
}
private < Request extends ActionRequest,
Response extends ActionResponse
> TransportAction<Request, Response> transportAction(GenericAction<Request, Response> action) {
.....
//actions是个action到transportAction的Map,这个映射关系是在节点启动时初始化的
TransportAction<Request, Response> transportAction = actions.get(action);
......
return transportAction;
}
そして、TransportActionを入力し、TransportAction#実行(リクエスト要求、ActionListenerのリスナー) - > TransportAction#(タスクのタスク、リクエスト要求、ActionListenerのリスナー)を実行 - > TransportAction#(タスクのタスク、文字列actionNameの、リクエスト要求、ActionListenerのリスナー)に進みます。関連するプラグは、プロセスの濾過作用を定義する場合TransportActionは、要求を処理するために要求フィルタチェーンを呼び出し、プロセス第行う論理カードは、次のようにロジックはTransportAction、フィルタ・チェーンの処理ロジックに入る処理します。
public void proceed(Task task, String actionName, Request request, ActionListener<Response> listener) {
int i = index.getAndIncrement();
try {
if (i < this.action.filters.length) {
//应用插件的逻辑
this.action.filters[i].apply(task, actionName, request, listener, this);
} else if (i == this.action.filters.length) {
//执行TransportAction的逻辑
this.action.doExecute(task, request, listener);
} else {
......
}
} catch(Exception e) {
.....
}
}
ここでこのTransportSearchAction例に相当TransportAction特定の標的に対する要求を検索し、層流に残り輸送層は完了し、次のセクションでは、詳細には、処理ロジックTransportSearchActionについて説明します。
3.1は、クエリを配布します
コード入力:TransportSearchAction#doExecute。
まず、特定のクエリを取得するための決意は、リモートクラスタとローカルクラスタ(クラスタ全体のリモートアクセスのためのクラスタ)を含むインデックスのリストを、含まれます。
final ClusterState clusterState = clusterService.state();
//获取远程集群indices列表
final Map<String, OriginalIndices> remoteClusterIndices = remoteClusterService.groupIndices(searchRequest.indicesOptions(),
searchRequest.indices(), idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState));
//获取本地集群indices列表
OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY);
if (remoteClusterIndices.isEmpty()) {
executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, Collections.emptyList(),
(clusterName, nodeId) -> null, clusterState, Collections.emptyMap(), listener,
clusterState.getNodes().getDataNodes().size(), SearchResponse.Clusters.EMPTY);
} else {
//远程集群的处理逻辑
.....
}
そして、executeSearchメソッド、コンストラクタの目的シャードのリストを入力し、私たちは見ることができます:
- 赤の状態で問い合わせることができます。
- インデックスのすべての部分をチェックするデフォルトのターゲットの必要性;
- デフォルトでは、クエリをQUERY_THEN_FETCH。
- 同時クエリの最大数は256を断片;
- デフォルトでは、オープン要求キャッシュではありません。
private void executeSearch(....) {
// red状态也可以查询
clusterState.blocks().globalBlockedRaiseException(ClusterBlockLevel.READ);
......
// 结合routing信息、preference信息,构造目的shard列表
GroupShardsIterator<ShardIterator> localShardsIterator = clusterService.operationRouting().searchShards(clusterState,
concreteIndices, routingMap, searchRequest.preference(), searchService.getResponseCollectorService(), nodeSearchCounts);
GroupShardsIterator<SearchShardIterator> shardIterators = mergeShardsIterators(localShardsIterator, localIndices,
searchRequest.getLocalClusterAlias(), remoteShardIterators);
.....
if (shardIterators.size() == 1) {
// 只有一个分片的时候,默认就是QUERY_THEN_FETCH,不存在评分不一致的问题
searchRequest.searchType(QUERY_THEN_FETCH);
}
if (searchRequest.allowPartialSearchResults() == null) {
// 用户未定义首选项,采用默认方式
searchRequest.allowPartialSearchResults(searchService.defaultAllowPartialSearchResults());
}
if (searchRequest.isSuggestOnly()) {
// 默认是没有开启请求缓存的
searchRequest.requestCache(false);
switch (searchRequest.searchType()) {
case DFS_QUERY_THEN_FETCH:
// 默认情况下DFS_QUERY_THEN_FETCH会转化成QUERY_THEN_FETCH
searchRequest.searchType(QUERY_THEN_FETCH);
break;
}
}
.....
// 最大并发分片数,最大是256:Math.min(256, Math.max(nodeCount, 1)* IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.getDefault(Settings.EMPTY))
setMaxConcurrentShardRequests(searchRequest, nodeCount);
boolean preFilterSearchShards = shouldPreFilterSearchShards(searchRequest, shardIterators);
// 生成查询请求的调度类searchAsyncAction并启动调度执行
searchAsyncAction(task, searchRequest, shardIterators, timeProvider, connectionLookup, clusterState.version(),
Collections.unmodifiableMap(aliasFilter), concreteIndexBoosts, routingMap, listener, preFilterSearchShards, clusters).start();
}
そして、クラスInitialSearchPhase SearchPhaseの実行により実現される方法に入る:横断されるシャードに基づきがある場合はシャードは、Nは、リストは、N回のリクエストを送信しているのと同じノードをシャードノードにクエリを送信し、要求はしません一つにマージします。
public final void run() throws IOException {
.....
if (shardsIts.size() > 0) {
// 最大分片请求数可以通过max_concurrent_shard_requests参数配置,6.5之后版本新增参数
int maxConcurrentShardRequests = Math.min(this.maxConcurrentShardRequests, shardsIts.size());
....
for (int index = 0; index < maxConcurrentShardRequests; index++) {
final SearchShardIterator shardRoutings = shardsIts.get(index);
// 执行shard级请求
performPhaseOnShard(index, shardRoutings, shardRoutings.nextOrNull());
}
}
}
shardsItsは、マスタースライスからこのクエリ、shardRoutings.nextOrNull()に関連するすべての断片は、一つまたはすべてのコピーを選択します。
クエリソースコード解析(2):次に続け。