I.はじめに
この記事では、Elasticsearch を使用する際にほとんどの人が遭遇する可能性のあるいくつかの落とし穴を、参照、議論、および補足のために挙げています。
2. ピット 1: ES は準リアルタイムですか?
このピットが実際のピットであるかどうかを確認するために、自分で手動でテストできます。
データが ES に更新され、戻りプロンプトが成功したら、すぐに ES を介してクエリを実行し、返されたデータが最新かどうかを確認します。
思考: クエリされたデータが最新の場合、ピットはピットとは見なされず、土で埋めることができますが、最新のデータでない場合、その背後にある理由は何ですか?
まだ検証を行っていなくても問題ありません。ES データのインデックス作成のプロセス全体を見てみましょう。そこから手がかりが見つかるかもしれません。
|| データ インデックス作成の全プロセス データ
インデックス作成の全プロセスには、ES フラグメンテーション、Lucene インデックス、セグメント、およびドキュメント間の関係などの知識ポイントが含まれます。
Lucene、Segment、Document の関係図は次のとおりです。
相互の関係:
- Lucene インデックスは複数のセグメントを格納できます。
- セグメントには複数のドキュメントを格納できます。
ES のフラグメントは Lucene インデックスであり、各 Lucene インデックスは複数のセグメントで構成されています。つまり、Lucene インデックスのサブセットはセグメントです。
|| データのインデックス作成プロセスの詳細な説明
-
新しいドキュメントが作成されると、データは最初に新しいセグメントに保存されますが、古いドキュメントは削除され、元のセグメントに削除マークが付けられます。ドキュメントが更新されると、ドキュメントの古いバージョンは削除済みとしてマークされ、ドキュメントの新しいバージョンは新しいセグメントに保存されます。
-
Shared が書き込み要求を受け取ると、その要求は Translog に書き込まれ、次に Document がメモリ バッファに格納され (注: メモリ バッファ内のデータは検索できません)、最後に Translog はすべての変更レコードを保存します。 .
-
1 秒ごと (デフォルト設定) に 1 回の更新操作が実行され、メモリ バッファー内のデータがセグメントに書き込まれ、ファイルシステム キャッシュに格納されます。このとき、新しいデータを検索できます。
上記のインデックス作成プロセスの説明から、結論を導き出すことができます。ES はリアルタイムではなく、1 秒の遅延があります。
実際のアプリケーションでは、遅延の問題をどのように解決すればよいのでしょうか?
簡単な方法: ユーザーに一定の遅延でデータをクエリするように促し、もう一度やり直してください。
3. ピット 2: ES がクラッシュして回復した後、データが失われる
上記の最初のピット ポイントで、1 秒ごと (デフォルト設定) に、メモリ バッファー内のデータがセグメントに書き込まれることを説明しました. この時点で、データのこの部分はユーザーが検索できますが、システムがダウンすると、データは失われます。
上の図の灰色のバケツ内のデータは検索できますが、永続化されておらず、ES がダウンすると、この部分のデータは失われます。
データはどのように失われますか?
この問題は、Lucene の commit 操作を使用することで簡単に解決できます。
コミットの特定の操作: 最初に複数のセグメントをマージしてディスクに保存し、次に灰色のバケットを緑色のバケットに変更します。
コミットの欠点: コミット間の ES ダウンタイムの問題を引き起こす IO 消費。translog fsync の前にシステムがダウンすると、データは直接失われます。
これは、データの整合性を確保するにはどうすればよいかという新たな疑問につながります。
Translog のデータはディスクに直接保存されず、fsync の後にのみ保存されるため、Translog を使用して問題を解決します。Translog には 2 つのソリューションがあります。
- 最初の方法: Index.translog.durability を request に設定します. システムが正常に動作していることがわかった場合は、この方法を使用できます.
- 2 番目の方法: Index.translog.durability を fsync に設定し、ES のシャットダウンが開始されるたびに、まずメイン データと ES データを比較し、ES の欠落データを見つけます。
ここで重要なポイントを強調する必要があることに注意してください: Translog はいつ fsync を実行しますか?
Index.translog.durability が request に設定されている場合、各リクエストは fsync されますが、これは ES のパフォーマンスに影響します。この時点で、Index.translog.durability を fsync に設定すると、すべてのリクエストが Index.translog.sync_interval ごとに 1 回 fsync されます。
3. ピット 3: ページングが深いほど、クエリの効率が低下する
ES ページング ピットの発生は、ES 読み取り操作要求の処理フローと密接に関連しているため、次の図に示すように、ES 読み取り操作要求の処理フローを深く分析する必要があります。
ES の読み取り操作プロセスは、主にクエリ フェーズとフェッチ フェーズの 2 つのフェーズに分けられます。
|| クエリ フェーズ
クエリ フェーズで調整されたノードは、最初にリクエストをすべてのシャードに配布し、次に各シャードがローカルでクエリを実行し、結果セット キューを構築し、ドキュメント ID と検索スコアをコマンドでキューに格納してから、調整ノード、そして最終的に調整ノードは、グローバル キューを構築し、受信したすべての結果セットをマージし、グローバル ソートを実行します。
クエリ フェーズを強調する必要があります。ES クエリ プロセス中に、検索が from パラメーターと size パラメーターを取得する場合、Elasticsearch クラスターはシャード番号 * (from + サイズ) 個のデータを調整ノードに返し、それらを単一のノードで並べ替える必要があります。サイズ データのサイズ。たとえば、クライアントが 10 個のデータ (3 つのフラグメントなど) を要求した場合、各フラグメントは 10 個のデータを返し、調整ノードは最終的に 30 個のデータをマージしますが、最終的に 10 個のデータのみをクライアント。
|| フェッチ フェーズ
調整ノードは、最初に結果セットのドキュメント ID に従ってすべてのフラグメントから完全なドキュメントを取得し、次にすべてのフラグメントが完全なドキュメントを調整ノードに返し、最後に調整ノードが結果をクライアントに返します。
ES 読み取り操作プロセス全体で、Elasticsearch クラスターは実際にはシャード番号 * (from + サイズ) 個のデータを調整ノードに返し、単一のマシンでそれらを並べ替え、最後にこのサイズのデータをクライアントに返す必要があります。
たとえば、シャードが 5 つある場合、10000 から 10010 (from=10000, size=10) の範囲の並べ替え番号を使用して結果をクエリする必要があります.各シャードが計算のために調整ノードに返すデータの量は? 10ではなく10010と言ってください。つまり、調整ノードはメモリ内で 10010*5=50050 レコードを計算する必要があるため、システムの使用では、ユーザーのページが深くなると、クエリの速度は遅くなります。つまり、ページが多いほど良いということです。
では、ES ページングの問題をより適切に解決するにはどうすればよいでしょうか。
パフォーマンスを制御するために、主に ES で max_result_window 構成を使用します。デフォルトは 10000 です。 from+size > max_result_window の場合、ES はエラーを返します。
システムを設計する際には、一般的にユーザーがページをめくりすぎないように制御する必要があることがわかりますが、これは実際のシナリオでユーザーに受け入れられるものであり、以前の計画でも採用された設計方法です。ユーザーが本当にページをめくる必要がある場合は、ES の search_after 関数を使用して解決できますが、ページ ジャンプを実現することはできません。
クエリは注文の合計金額に応じてページングされます. 前のページの最後の注文の合計金額は 10. 次に、次のページのクエリ サンプル コードは次のとおりです: (search_after 値は、並べ替えの結果の値です)最後のクエリ結果のフィールド)
{
"query":{
"bool":{
"must":[
{
"term":{
"user.user_name.keyword":"李大侠"
}
}
],
"must_not":[
],
"should":[
]
}
},
"from":0,
"size":2,
"search_after":[
"10"
],
"sort":[
{
"total_amount":"asc"
}
],
"aggs":{
}
}