elasticsearchがスケールする仕組み(参照編)
elastcisearchのスケール性能について調査したことをまとめました。
シャーディングとレプリケーション
elasticsearchはスケールアウトの仕組みにシャーディングとレプリケーションを採用しています。
シャーディングはデータを分割して保存することで参照を分散する仕組みです。
レプリケーションはデータ集合の複製を作成することで、同じデータにアクセスするリクエストを並列に実行したり可用性を高めたりできる仕組みです。
elasticsearchはインデックスをシャードに分割しつつ、それぞれのシャードをレプリケーションしています。
query phaseとfetch phase
elasticsearchの検索のプロセスはquery phaseとfetch phaseに別れています
query phase
elasticsearchクラスタは複数のノードで構成されますが、インデックスや検索などのリクエストはどのノードも受けることができます。
リクエストを受け取ったノードはcoodinating node
と呼ばれます。
検索リクエストを受けると、coodinating node
が各シャードのプライマリ or レプリカに検索リクエストを行います。
各シャードは size + from
件のドキュメントを検索ソートし、リクエストを受け取ったノードに返します。(この段階ではドキュメントのIDとソート対象になるvalueをのみを返します。)
、coodinating node
は各シャードから返ってきた結果をマージ、ソートします。
fetch phase
query phaseで入手した情報を利用して、リクエストを行う必要があるシャードのみに対してmulti getリクエストを行います。
各シャードはドキュメントの内容を読み込んでcoodinating node
返します。
coodinating node
が各ノードから得たドキュメント情報をマージ し、最終的な結果をクライアントに返します。
シャード数とレプリカ数について
ノード数をいくら増やしてもシャード数やレプリカ数を増やさなければ処理は分散されません。
elasticsearchの性能を引き出すにはこれらの値を適切に設定する必要があります。
プライマリ/レプリカシャードは自動的に各ノードにいい感じに分散するように配置されます。
インデックスを複数のシャードに分割しているほど、1リクエストの処理が分散され、並列に行われます。
また、シャードのレプリカ数が多ければ、複数のリクエストの並列度が上がります。
ただしレプリカ数は多ければ多いほど良いわけではなく、レプリカ数 > ノード数になるとひとつのノードにシャードが重複するため意味はなくなってきます。
ボトルネックになりそうな箇所
単純に検索処理の負荷が大きい場合(複雑なaggregationなど)以外の要因を書きます。
ヒットする件数が多すぎる
query phaseで各シャードから取得した結果をソートする処理はcoodinating node
1台で行われるためスケールアウトできません。
そのため検索結果の件数が多いことがボトルネックになってしまう可能性があります。
deep paging
query phaseにおいて各シャードでsize + from
件の検索・ソートを行うため、fromが大きくなると処理の負担が大きくなり、ボトルネックになる可能性が出てきます。
このような検索はdeep pagingと呼ばれます。
対策
インデックスを分割する
インデックス内のドキュメントが増えるほど検索時の負荷も際限なく増えることになるため、インデックスを日付毎にするなど時系列で分割するといった方法を取ることが多いです。
index templateでマッピングをあらかじめ登録しておくと、時系列で分割されたインデックスの生成がやりやすくなります。
scroll searchやsearch afterを使う
elaticsearchはscroll searchやsearch afterなど、deep paging対策の仕組みを提供しています。
search afterはほぼfromと同様のことができるので、使えないか検討するべきだと思います。
参考にしたドキュメント
古いですが基本的な処理の流れは今も変わってないと思います。
Query Phase | Elasticsearch: The Definitive Guide [2.x] | Elastic
Fetch Phase | Elasticsearch: The Definitive Guide [2.x] | Elastic