...

MySQL EXPLAIN ANALYZE:パフォーマンスを最大化するためのクエリの正しい解釈

mysql explain を使って、MySQL 8 がどのように実行プランを 実行する そして、その過程でどのステップが測定可能な時間を要するのか。そうして、実際の実行時間、行数、ループを基に、どこで計画を調整すべきかを見極め、 パフォーマンス 私の検索件数を的を絞って増やしていく。.

中心点

すぐに要点を把握できるよう、最も重要な学習目標を簡潔にまとめ、それにふさわしい 優先順位. 。計画の各行には物語が込められており、私があなたに本当に注目すべき点を示します achtest. 各項目を読み、クエリを確認し、その知見を直接チューニングの手順に反映させてください。.

  • 実際の稼働時間: EXPLAIN ANALYZE はクエリを実行し、各ステップごとの所要時間を測定します。.
  • 予測と現実: 大きな乖離が見られる場合は、統計データに誤りがあるか、インデックスが欠落していることを示しています。.
  • TREE形式: ツリー形式のプランでは、イテレータ、フィルター、および結合が可視化されます。.
  • ホットスポット: 「time to last row」が長く、ループが多い箇所がチューニングの目標となります。.
  • インデックス戦略: 適切な(複合的なものも含む)インデックスを採用することで、コストを大幅に削減できる。.

このリストは、あなたに明確な 方向, 、しかし、実際に計画書を読み込んで初めて、その知識を効果的に活用できるのです。その直後に、私が各指標をどのように評価し、次にどのような ステップ そこから私は次のように推論する。.

EXPLAIN 対 EXPLAIN ANALYZE:実際に測定されているのは何か

従来のEXPLAINを使用すると、オプティマイザが計画した実行パス、つまり 草案 推定コストと行数を明記したもの。このプランからは、テーブルの順序、使用されるインデックス、および結合戦略がわかりますが、実際の 測定値. EXPLAIN ANALYZE は処理を続け、実際にクエリを実行し、最初の行から最後の行までの所要時間やループ回数を測定します。これにより、ツリー内のどのノードが最も時間を費やしているか、そしてどこから手をつけるべきかがすぐにわかります。こうして、推測ではなく測定結果に基づいた判断ができるようになります。 データ そして、根拠に基づいた最適化の判断を下します。.

構文と代表的な使用例

簡単なコマンドから分析を始めます: EXPLAIN ANALYZE SELECT ..., 、それを使えばすぐに ランタイム 各ノードごとに受け取る。TREE形式の出力には、スキャン、結合、ソート、フィルタなどのイテレータが、推定値および実際の ライン. 私はこれを特に、繰り返し発生する問題の照会、複数のテーブルを対象としたUPDATE/DELETE、およびORDER BYやGROUP BYを含むステートメントで活用しています。また、必要に応じて、 FORMAT=JSON, 、コストモデルを深く掘り下げたい場合にはそうですが、日常的なチューニングにはたいていこのツリーで十分です。オプティマイザーに関する問題をさらに深く掘り下げたい方には、以下の記事が参考になるでしょう。 オプティマイザーの詳細, 、私が実際に活用しているものです。.

TREEプランの読み方

私は、各ノードを、データを生成したり、 フィルタリングする. スキャンはテーブルやインデックスから行を取得し、結合はストリームを連結し、フィルタは行数を絞り込み、ソートは行を並べ替えたりグループ分けしたりする。 結果. 。「rows (actual/estimated)」、「time to first row」、「time to last row」、「loops」の各フィールドは、私にとって最も重要な指標です。 実際の行数が推定値から大きく外れている場合は、統計情報やインデックスを修正します。「time to last row」が極端に長引いている場合は、後段のソート処理、大規模な結合、あるいは不適切な フィルター.

主要指標の理解:推定から現実へ

主な指標をわかりやすい表にまとめておきますので、典型的なシグナルを素早く 認識する. 各行には、各指標の意味、私が注目している警告サイン、そして通常どのような対策が講じられるかが記載されています。 ヘルプ.

キーパーソン 意味 警告信号 チューニングの手法
行数(予想/実績) 計画値と実績値 ライン 大きな乖離(例:10 対 100,000) 統計情報を更新し、不足している インデックス チェック
最初の行までの時間 初回までの残り時間 問題 結果の数が少ないにもかかわらず、徐々に進んでいる 開始ノードの確認、初期フィルタ 強化する
最終行までの時間 の総所要時間 ノードの 「first row」よりもかなり高い„ ソート、結合戦略、ストリーム 減らす
ループ ~の頻度 復習 非常に多くの反復 JOINの再配置、サブクエリ 成形する

演算子の正しい解釈:スキャン、結合、ソート

私はどの イテレータ 実際に仕事をしているのは:

  • インデックス範囲スキャン/一意スキャン: 選択的なWHERE条件と一致する接頭辞がある場合に最適です。「time to first row」は短く、「time to last row」は結果の件数によって異なります。.
  • テーブルスキャン: 大規模なテーブルで警告が表示された場合、適切なフィルター、複合インデックス、またはクエリの書き換えを検討します。.
  • ネストされたループ結合: 標準戦略。「ループ」が多数発生している場合は、不適切なドライバを使用しているか、内部テーブルにインデックスが設定されていないことを示唆しています。.
  • ハッシュ結合 (MySQL 8):大規模で均等に分散されたEqui-Joinに適している。「time to first row」は長くなる可能性がある(ビルドフェーズ)が、サンプル数が多ければ「time to last row」のパフォーマンスが向上する。.
  • 並べ替え/グループ: TREE では、独立したノードとして明確に表示される。実行時間が長い場合は、インデックスによるサポートが不足していることを示していることが多い。.
  • フィルター: 後段のフィルタは、インデックス・コンディション・プッシュダウンやそれ以前の選択の機会を逃したことを示している。.

Sortノードが「time to last row」を支配している場合、インデックスを使用して目的の順序を実現できるかどうかを確認します。例えば、次のようにして カバーリング- 適切なソート順序を持つインデックス。ORDER BY がインデックスの定義(方向、プレフィックス)と一致する場合、ソート処理が完全に省略されることがよくあります。.

測定方法:公平に比較する方法

測定は1回だけではありません。キャッシュ効果によって結果が歪められる可能性があるため、次のようにします:

  • EXPLAIN ANALYZE を数回実行し、単一の値ではなく、中央値や範囲を評価します。.
  • 私は「コールド」キャッシュと「ウォーム」キャッシュを区別しています。ウォームな測定結果からは、ユーザーが最初の実行後にどのような体験をするかがわかります。.
  • 単純な例だけで計画がうまく見えるだけにならないよう、代表的なパラメータを変化させています。.
  • 後で結果を遡って確認できるよう、スキーマとデータの状態を記録しています。.

DML文(UPDATE/DELETE)では、トランザクションを使用しています: START TRANSACTION; EXPLAIN ANALYZE UPDATE ...; ROLLBACK;. これにより、永続的な変更を加えずに実際の測定値を得ることができます。重要:EXPLAIN ANALYZE 導く そのため、本番環境では慎重に使用しています。.

統計とデータの分布:推定誤差の是正

「estimated」行と「actual」行の間に大きな差が生じるのは、データの分布が偏っていることが原因であることが多い。その場合、私は2つのアプローチを並行して進める:

  • 統計情報を更新する: オプティマイザーが最新の情報を持てるようにします。最新の統計情報があれば、ジョインやインデックスの選択が改善されます。.
  • ヒストグラムの活用: スキューの大きいカラムの場合、ヒストグラムを活用することで、選択性をより現実的に推定することができます。その結果、EXPLAIN ANALYZE では、推定値と実際の値との差が明らかに縮小します。.

更新後も推定値が外れ続ける場合は、最も選択性の高い述語の順に複合インデックスを検証し、列間の相関関係を調べます。目的は、コストのかかる演算子に、事前に十分にフィルタリングされた行を、できるだけ早い段階で少数だけ投入することです。.

セミジョイン戦略とサブクエリ

MySQL 8 では、IN/EXISTS 述語がしばしばセミジョイン計画に変換されます。TREE では、これが Materialization、FirstMatch、または Loose Index Scan として表示されます。私が注目しているのは:

  • マテリアライゼーション: サブセットは一度構築すれば、繰り返し再利用できます。規模がそれほど大きくない場合に適しています。.
  • FirstMatch: 最初のヒットで早めに停止する――アウター行ごとのヒット数が少ないと予想される場合は、ループ回数を節約できる。.
  • ルーズ・インデックス・スキャン: インデックスを使用したDISTINCTに類似したパターンにおいて非常に効率的です。.

外側のテーブルの各行ごとに実行されるサブクエリは、「ループ」を肥大化させます。私はそれらをJOINに書き換えるか、意図的にマテリアライズ(CTE/派生テーブル)することで、実行計画が一度だけコストのかかる処理を行い、その後は低コストで参照できるようにしています。.

SQLの的を絞った最適化:ステップバイステップ

まずはインデックス戦略から始め、頻繁に使用されるWHERE句やJOIN条件を次のように最適化します。 インデックス 。フィルタや並べ替えで複数の列が必要な場合は、複合インデックスを設定し、列の順序を最も頻度の高いものに合わせます。 述語. 。その後、ループ内で実行されるサブクエリについては、書き換えたり、ジョインに変換したりして、負荷を軽減します。 SELECT * を具体的な列に置き換え、データ移動量を減らして実行計画の負荷を軽減します。その後、統計情報を最新の状態に保ちます。なぜなら、不正確な推定値はオプティマイザを誤った方向に導いてしまうからです。 アベレージ.

索引の実践:カバリング、順序、実験

私は、EXPLAIN ANALYZE で即座に確認できる 3 つのシンプルなレバーを利用しています:

  • カバーリング指数: インデックスに必要なすべての列(フィルタ、結合、投影)が含まれている場合、実行計画はテーブル検索を省略できます。「time to last row」が大幅に短縮されることがよくあります。.
  • 列の順序: 選択性と利用種別に基づいて並べ替えます(並べ替えの前にフィルタリングを行います)。ORDER BY/GROUP BY では、正しい順序と適切な接頭辞を使用します。.
  • インデックス実験: 一時的な、, 目に見えない インデックスについては、既存の実行計画を不安定にすることなく、オプティマイザがそれを選択するかどうかをテストします。実行計画が改善される場合は、そのインデックスを恒久的に有効にします。.

複数の候補インデックスが存在する場合は、EXPLAIN ANALYZE を使用して実行計画を比較し、一貫して「time to last row」を測定します。判断に迷った場合は、さまざまなパラメータ値において実行時間が最も安定している実行計画を採用します。.

実践例:計画を分析し、指標を設定し、成果を測定する

よくある質問を一つ取り上げます: EXPLAIN ANALYZE SELECT o.id, o.date, c.name FROM orders o JOIN customers c ON c.id = o.customer_id WHERE o.date >= '2025-01-01' ORDER BY o.date DESC; そして、まずそのテーブルのノードを確認してください 注文. プランで実際の行数が多く、フルテーブルスキャンが行われると報告された場合、私は適切なインデックスを作成します。例えば、 orders(date, customer_id). 。その後、変更前後の「time to last row」を比較します。この数値が全体的な効果を非常に明確に示しているからです ショー. ORDER BY がインデックスの順序と一致すれば、ソート処理を省略でき、総処理時間を大幅に短縮できます。このようにして、漠然とした推測ではなく、測定値に基づいて進捗を確認しています。 印象.

DML文を確実に分析する

データセットを変更するUPDATE/DELETEについては、体系的な手順で進めています:

  • 測定処理をトランザクションにカプセル化し、単に測定したいだけの場合はロールバックします。.
  • トリガーや制約によって追加コストが発生していないかを確認しています。EXPLAIN ANALYZE を実行すると、影響を受けるノードで処理時間が長くなっていることがわかります。.
  • 私は「affected rows」と「rows actual」の比率に注目しています。この比率が悪い場合は、フィルタリングが遅すぎるか、インデックスが不足していることを示しています。.

複数テーブルに対するUPDATE文では、結合順序とインデックスのカバレッジが極めて重要です。ソート/結合ノードにおける「time to last row」が長い場合は、インデックスの改善余地があるか、あるいは中間結果の保存を伴う2つの適切なステートメントに書き換える余地があることを示唆しています。.

ホスティングがクエリのパフォーマンスに与える影響

私はデータベースを単独のものとして捉えていません。なぜなら、メモリ、I/O、CPUがすべてに大きな影響を与えるからです。 ランタイム. 高速なSSDは読み取り時の待ち時間を短縮し、十分なRAMはバッファプールを拡大し、堅牢なCPUスタックはソートや集計などの処理を高速化し、 参加. 本番環境では、データ集約型のワークロードをうまく処理できるホスティング構成を好んで採用しています。オプティマイザーに関するトピックについては、以下の情報も参考になります。 オプティマイザー内部, 、これを補足的な視点として活用しています。明確な計画と強力な環境を組み合わせることで、 応答時間.

文脈におけるリソースと演算子

計画を読む際は、メモリを大量に消費するノードに注目しています。 大規模なソートやハッシュ結合にはメモリが必要であり、そのサイズが大きすぎると、一時テーブルに迂回されます。TREEでは、これは処理が遅いノードや、「time to first row」と「time to last row」の間に明らかな差があることから確認できます。それに対して、私は次のように対応します:

  • 入力データの量を削減する(フィルタを前段に配置する、より優れた結合ドライバを使用する)。.
  • 重複を避けるため、希望する順序でのインデックス対応を改善しました。.
  • データ量に適した結合タイプ(ネストループ対ハッシュ)であるかを確認する。.

特にレポート実行の際には、EXPLAIN ANALYZE をミニスナップショットではなく、代表的なデータに対して実行するようにしています。そうして初めて、測定値が実際の負荷を反映するからです。.

日常生活におけるベストプラクティス

まず、ログで目につくクエリや、ユーザーから頻繁に「遅い」と報告されているクエリを分析します 報告する. 次に、EXPLAIN ANALYZE を使って測定し、重要な数値を記録した上で、推定値と実際の結果を比較します。その結果に基づき、インデックスやクエリの記述を的を絞って変更し、変更前後の状況を記録することで、進捗状況を明確に把握できるようにします。 作る. 私は、生産上の問題が発生するのを待つのではなく、開発プロセスの早い段階でこれらの分析を計画に組み込んでいます。繰り返しレビューを行うことで、パターンをより早く見極め、より的確に判断を下すことができます。 チューニング-対策。

計画の迅速化に向けた実用的なチェックリスト

  • 見積値と実績値 おおむね一致していますか?もし一致しない場合は、統計データやヒストグラムを確認してください。.
  • あるノードが「time to last row」を支配しているか? 最初のチューニング対象(インデックス、結合の選択、ソートの回避)。.
  • 「ループ」の回数が多いですか?内側のテーブルのJoinドライバーやインデックスを改善するか、セミジョインを利用してください。.
  • 後段のソートやグループ化はありますか?インデックスの順序と方向を、ORDER BY/GROUP BYに合わせて調整してください。.
  • そのクエリには本当にすべての列が必要ですか?カバーインデックスを構築できるよう調整し、SELECTリストを簡潔にしましょう。.
  • 行ごとのサブクエリ? JOIN に変換するか、マテリアライズしてください。.
  • パラメータを超えて安定しているか? 複数の現実的な値を用いて測定する。.

よくある誤解とそれを避ける方法

私は推定値を盲目的に信頼したりはしません コスト, 、実際の行数が著しく異なる場合は。同様に、「time to first row」から安易な結論を導き出すことはせず、「time to last row」が主な負荷となっている場合は 運ぶ. 開始時の処理が速くても、最終的にソートや結合が処理の大部分を占めてしまっては意味がありません。また、ループについては徹底的にチェックしています。なぜなら、ループの中には、非効率な結合や、行ごとに実行されるサブクエリが隠れていることがよくあるからです。実行計画、測定値、データの分布がすべて一致して初めて、私は変更を加えます もの.

特殊なケース:CTE、派生テーブル、パーティション

共通テーブル式(CTE)や派生テーブルは、マテリアライズまたはマージすることができます。TREEでは、マテリアライズを独立した構築ステップとして認識しています。これは、部分ストリームが複数回使用される場合や、計算コストが高い場合に有効です。 CTEが1回しか使用されず、かつ選択性が高い場合は、追加のメモリ操作が不要になるため、マージの方が効率的であることが多いです。「time to first row」が大幅に増加していないかを確認しています。増加している場合は、マテリアライゼーションが過剰になっている可能性があります。.

パーティション化されたテーブルは、述語によってパーティションが明確に絞り込まれる場合、大量のデータを扱う際に役立ちます。 実行計画を確認し、プルーニングが適用されているか(スキャンされるパーティションがごくわずかであるか)を確認します。プルーニングが行われていない場合、コストはすべてのパーティションに分散されます。これは、パーティションキーを最も頻度の高いフィルターに合わせて調整するか、プルーニングが可能になるようにクエリを再構成すべきであることを示唆しています。.

簡単にまとめると

EXPLAIN ANALYZE を使って MySQL の実行計画を定量的に分析し、以下の方法でホットスポットを特定します。 インデックス, 、クエリの書き換え、および最新の統計情報を修正します。私は、推定行数と実際の行数の差異、最初の行から最後の行までの所要時間、および ループ. 。そこから、数少ない効果的な対策を導き出し、EXPLAIN ANALYZE を使って各効果を改めて検証します。時間が経つにつれて、パターンを即座に見極められるようになり、適切な対策をより迅速に実施できるようになります。こうして、 パフォーマンス 信頼性が高く、クエリを長期的に安定した状態に保ちます。.

現在の記事

最新鋭のデータセンターに設置された、CloudLinuxホスティングとPHPセレクターを備えたサーバーラック
サーバーと仮想マシン

CloudLinux PHP Selector – 実際の運用における仕組みと限界

CloudLinux PHP Selector の解説:ホスティング環境で PHP のバージョンを管理し、拡張機能を有効化し、制限値を安全に調整する方法――最新の共有ホスティング環境に最適です。.