Elasticsearchの一般的なパフォーマンスボトルネックのトラブルシューティング

インデキシング、検索、ヒープ、ストレージ、シャード設計におけるElasticsearchのパフォーマンスボトルネックを見つけるための実践的なワークフロー。

Elasticsearchの一般的なパフォーマンスボトルネックのトラブルシューティング

Elasticsearchのパフォーマンスボトルネックのトラブルシューティングは、最初の簡単な理論に抵抗するのが最善です。遅いダッシュボードは悪いクエリかもしれませんが、ホットシャード、飽和したディスク、ヒープの問題、マッピングのミス、またはI/Oを競合するリカバリプロセスである可能性もあります。証拠から始めて、範囲を絞り込みます。

私は通常、問題を3つの部分に分けます:何が遅いのか、どこで遅いのか、何が変わったのか。「Elasticsearchが遅い」というのは実行可能ではありません。「logs-prod-*の検索レイテンシが昨日のマッピング変更後に2倍になり、主に2つのデータノードで発生している」というのは、作業の余地があります。

パフォーマンス問題の診断

具体的な解決策に飛び込む前に、パフォーマンス問題を診断するためのツールと方法を持つことが不可欠です。Elasticsearchは、このプロセスに非常に役立ついくつかのAPIとメトリクスを提供しています。

主要なツールとメトリクス:

  • クラスタヘルスAPI(_cluster/health): クラスタのステータス(green、yellow、red)、ノード数、シャード数、保留中のタスクの概要を提供します。保留中のタスクの数が多い場合は、インデキシングまたはリカバリの問題を示している可能性があります。
  • ノード統計API(_nodes/stats): CPU使用率、メモリ、ディスクI/O、ネットワークトラフィック、JVMヒープ使用量など、各ノードの詳細な統計を提供します。これはリソースに制約のあるノードを特定するために重要です。
  • インデックス統計API(_stats): インデックスレート、検索レート、キャッシュ使用量など、個々のインデックスの統計を提供します。これにより、問題のあるインデックスを特定できます。
  • スローログ: Elasticsearchは、遅いインデキシングおよび検索リクエストをログに記録できます。スローログのしきい値はインデックス設定であるため、クラスタ全体をログジェネレーターに変えることなく、1つのノイズの多いインデックスに適用できます。
    • インデキシングスローログ: バルク書き込みが一時停止したり、インジェストレイテンシが急上昇した場合に便利です。
    • 検索スローログ: レイテンシチャートだけでなく、実際のリクエストパターンが必要な場合に便利です。
  • 監視ツール: Kibanaの監視UI、Elasticsearch Exporterを使用したPrometheus、または商用APMツールなどのソリューションは、詳細な分析のためのダッシュボードと履歴データを提供します。

一般的なボトルネックと解決策

1. 遅いインデキシング

遅いインデキシングは、ネットワークレイテンシ、ディスクI/Oボトルネック、リソース不足、非効率的なマッピング、または最適でないバルクAPIの使用など、さまざまな要因によって引き起こされる可能性があります。

原因と解決策:
  • ディスクI/Oの飽和: Elasticsearchはインデキシングにおいて高速なディスクI/Oに大きく依存しています。SSDが強く推奨されます。

    • 診断: _nodes/statsまたはOSレベルのツールを使用して、ディスクの読み取り/書き込みIOPSとスループットを監視します。高いキューの深さを探します。
    • 解決策: より高速なストレージ(SSD)にアップグレードするか、シャードをより多くのノードに分散させるか、シャード戦略を最適化してノードあたりのI/Oを削減します。
  • JVMヒープの圧迫: JVMヒープが常に圧迫されている場合、ガベージコレクションが大きなボトルネックになり、インデキシングを含むすべての操作が遅くなる可能性があります。

    • 診断: Kibana Monitoringまたは_nodes/statsでJVMヒープ使用量を監視します。ヒープ使用量が高く、頻繁で長いガベージコレクションの一時停止は危険信号です。
    • 解決策: JVMヒープサイズを増やします(ただし、システムRAMの50%を超えず、30.5 GBを超えないようにします)。マッピングを最適化してドキュメントサイズを削減するか、ノードを追加して負荷を分散します。
  • 非効率的なマッピング: 過度に複雑なマッピング、多くの新しいフィールドが作成される動的マッピング、または不適切なデータ型は、インデキシングのオーバーヘッドを増加させる可能性があります。

    • 診断: インデックスマッピング(_mapping API)を分析します。ネストされたオブジェクト、多数のフィールド、または不必要にインデックスされたフィールドを探します。
    • 解決策: 適切なデータ型を使用して明示的なマッピングを定義します。該当する場合はdynamic: falseまたはdynamic: strictを使用します。必要でない限り、深くネストされた構造を避けます。
  • ネットワークレイテンシ: ノード間またはクライアントとクラスタ間のレイテンシが高いと、バルクインデキシングリクエストが遅くなる可能性があります。

    • 診断: クライアント/ノード間のネットワークレイテンシを測定します。バルクAPIの応答時間を分析します。
    • 解決策: クラスタノードを低レイテンシのプライベートネットワークに配置し、可能な場合はバルククライアントをクラスタの近くに配置し、不要なリージョン間トラフィックを削減します。リクエストキャッシュ設定はネットワークレイテンシを修正しません。
  • 最適でないバルクAPIの使用: バルクリクエストの代わりに個々のリクエストを送信したり、過度に大きい/小さいバルクリクエストを送信したりすると、非効率的になる可能性があります。

    • 診断: バルクインデキシングのスループットを監視します。バルクリクエストのサイズを分析します。
    • 解決策: すべてのインデキシング操作にバルクAPIを使用します。バルクサイズを実験して(通常、バルクリクエストあたり5〜15 MBが良い出発点です)、スループットとレイテンシの最適なバランスを見つけます。バルクリクエストが適切にバッチ処理されていることを確認します。
  • トランザクションログの耐久性: index.translog.durability設定は、トランザクションログがディスクにフラッシュされる頻度を制御します。request(デフォルト)はより安全ですが、asyncと比較してパフォーマンスに影響を与える可能性があります。

    • 診断: これは構成設定です。
    • 解決策: 最大のインデキシングスループットを得るには、async耐久性を検討してください。ただし、フラッシュ間のノードクラッシュが発生した場合、データ損失のリスクが高まることに注意してください。

2. 遅いクエリ

クエリパフォーマンスは、シャードサイズ、クエリの複雑さ、キャッシング、および基礎となるデータ構造の効率に影響されます。

原因と解決策:
  • 大きなシャード: シャードが大きすぎると、Elasticsearchがより多くのデータを検索し、より多くのセグメントから結果をマージする必要があるため、クエリが遅くなる可能性があります。

    • 診断: _cat/shardsまたは_all/settings?prettyを使用してシャードサイズを確認します。
    • 解決策: シャードサイズを10GB〜50GBにすることを目指します。データをより小さなシャードを持つ新しいインデックスに再インデックスするか、インデックスライフサイクル管理(ILM)を使用して経時的にシャードサイズを管理することを検討します。
  • シャードが多すぎる: 多数の小さなシャードが存在すると、特に検索中にクラスタのオーバーヘッドが高くなる可能性があります。各シャードは管理のためにリソースを必要とします。

    • 診断: _cat/shardsを使用して、ノードおよびインデックスごとのシャードの総数をカウントします。
    • 解決策: 可能であればインデックスを統合します。データモデルを最適化してインデックスの数を減らし、結果としてシャードの総数を減らします。時系列データの場合、ILMはシャード数の管理に役立ちます。
  • 非効率的なクエリ: 複雑なクエリ、大量のスクリプトを含むクエリ、用語の先頭でのワイルドカード検索、または正規表現は、非常にリソースを消費する可能性があります。

    • 診断: プロファイルAPI(_search?profile=true)を使用してクエリ実行時間を分析し、遅い部分を特定します。スローログを分析します。
    • 解決策: クエリを簡素化します。先頭のワイルドカードと高価な正規表現を避けます。可能な場合は、完全一致にmatchの代わりにtermクエリを使用します。タイプアヘッドの提案にはsearch_as_you_typeまたはcompletionサジェスターの使用を検討します。フィルター句を最適化します(スコアリングされないクエリにはqueryコンテキストの代わりにfilterコンテキストを使用します)。
  • キャッシングの欠如: 不十分または効果のないキャッシングは、繰り返しの計算とデータ取得につながる可能性があります。

    • 診断: _nodes/stats/indices/query_cacheおよび_nodes/stats/indices/request_cacheを使用して、クエリキャッシュとリクエストキャッシュのヒット率を監視します。
    • 解決策: 適切なキャッシングが有効になっていることを確認します。フィルターキャッシュ(クエリキャッシュの一部)は、繰り返しのフィルタークエリにとって特に重要です。頻繁に実行される同一のクエリについては、リクエストキャッシュを有効にすることを検討します。
  • セグメントマージのオーバーヘッド: Elasticsearchは、バックグラウンドで小さなセグメントをより大きなセグメントにマージします。このプロセスはI/OとCPUリソースを消費し、リアルタイムのクエリパフォーマンスに影響を与える可能性があります。

    • 診断: _cat/segmentsを使用して、シャードあたりのセグメント数を監視します。
    • 解決策: マージ設定を軽率に変更しないでください。大規模なバックフィル中は、リフレッシュ頻度を減らし、バルク同時実行性を制御し、マージスロットルとディスクI/Oを監視します。フォースマージは通常、アクティブなホットインデックスではなく、読み取り専用インデックス用です。

3. リソース競合(CPU、メモリ、ネットワーク)

リソース競合は広範なカテゴリであり、インデキシングとクエリの両方のパフォーマンス低下として現れる可能性があります。

原因と解決策:
  • CPU過負荷: 高いCPU使用率は、複雑なクエリ、集中的な集計、多すぎるインデキシング操作、または過剰なガベージコレクションによって引き起こされる可能性があります。

    • 診断: ノードごとのCPU使用率を監視します(_nodes/stats)。どの操作が最もCPUを消費しているか(例:検索、インデキシング、JVM GC)を特定します。
    • 解決策: クエリと集計を最適化します。負荷をより多くのノードに分散します。CPUを圧倒している場合はインデックスレートを減らします。GCオーバーヘッドを最小限に抑えるために、適切なJVMヒープ設定を確保します。
  • メモリの問題(JVMヒープとシステムメモリ): JVMヒープが不十分だと、頻繁なGCが発生します。システムメモリが不足するとスワッピングが発生し、パフォーマンスが大幅に低下します。

    • 診断: 各ノードのJVMヒープ使用量と全体的なシステムメモリ(RAM、スワップ)を監視します。
    • 解決策: 十分なJVMヒープを割り当てます(例:システムRAMの50%、最大30.5GB)。十分な空きシステムメモリを確保してスワッピングを回避します。ノードを追加するか、特定のロール(マスター、データ、インジェスト)に専用ノードを使用することを検討します。
  • ネットワークボトルネック: 高いネットワークトラフィックは、ノード間通信、レプリケーション、およびクライアントリクエストを遅くする可能性があります。

    • 診断: ノードとクライアント間のネットワーク帯域幅使用量とレイテンシを監視します。
    • 解決策: ネットワークインフラストラクチャを最適化します。不要なデータ転送を削減します。最適なシャード割り当てとレプリケーション設定を確保します。
  • ディスクI/Oの飽和: インデキシングで述べたように、これはディスクからデータを読み取るときにクエリパフォーマンスにも影響します。

    • 診断: ディスクI/Oメトリクスを監視します。
    • 解決策: より高速なストレージにアップグレードするか、データをより多くのノードに分散させるか、読み取るデータ量を減らすためにクエリを最適化します。

パフォーマンスチューニングのベストプラクティス

  • 継続的に監視する: パフォーマンスチューニングは継続的なプロセスです。クラスタの健全性とリソース使用率を定期的に監視します。
  • マッピングを最適化する: データに合わせて、明示的で効率的なマッピングを定義します。不要なフィールドやインデキシングを避けます。
  • シャード戦略: 最適なシャードサイズ(10〜50GB)を目指し、シャードが多すぎたり少なすぎたりしないようにします。
  • バルクAPIを使用する: インデキシングにはバルクAPIを、独立した検索をバンドルする必要がある場合はマルチサーチAPIを使用します。
  • JVMヒープをチューニングする: 十分なヒープを割り当てますが、過剰に割り当てないでください。スワッピングを避けます。
  • クエリパフォーマンスを理解する: クエリをプロファイリングし、簡素化し、フィルターコンテキストを活用します。
  • キャッシングを活用する: クエリキャッシュとリクエストキャッシュが効果的に使用されていることを確認します。
  • ハードウェア: ストレージにはSSDを使用し、十分なCPUとRAMを確保します。
  • 専用ノード: ワークロードを分離するために、マスター、データ、インジェストのロールに専用ノードを使用することを検討します。
  • インデックスライフサイクル管理(ILM): 時系列データの場合、ILMはインデックスの管理、シャードのロールオーバー、および古いデータの最終的な削除に不可欠であり、シャード数とサイズの制御に役立ちます。

ボトルネックを見つけたら、それに直接対処する最小の変更を行います。クラスタが本当に容量不足の場合にノードを追加します。ヒープが無駄になっている場合にマッピングを修正します。プロファイル出力が高価な句を指している場合にクエリを書き換えます。1つのノードが分散されるべき作業を行っている場合にシャード戦略を調整します。この規律により、パフォーマンス作業が無関係なチューニングノブの山になるのを防ぎます。