はじめに
最近学んでいるSQLのパフォーマンスチューニングについて簡単に記事にしてきます!
本記事ではSQLのパフォーマンスを意識する上で重要な実行計画の確認をしていきます![]()
SQLが実行される流れ
SQLが実行されると後はデータベース管理システム(DBMS)があれやこれやと働いて結果を返してくれます。
ではSQLが実行されてからDBMSは一体何をしているのでしょうか?
主に以下のことをしてくれています🦑
①パーサ
SQL文の構文解析が行われます。
構文が正しいかどうかをチェックします。
※この段階で文法エラーがあればここで止まります。
②プラン生成
構文解析されたクエリに対して、「どういう方法でデータを取得するか(実行計画)」の候補が複数作られます。
たとえば「インデックスを使う」「フルスキャンをする」などのパターンです。
③コスト評価
②で生成された複数の実行計画のコストを見積もり、最も効率が良いと判断されたものを選びます。
④統計情報(カタログ)
テーブルの行数、インデックスの有無、カラムの値の分布などの情報を管理しており、オプティマイザが実行計画を立てる際に参照されます。
⑤実行
最終的に選ばれた実行計画に従って、実際のデータ操作が行われます。
このステップで初めて、テーブルにアクセスしたり、結果が返ってきたりします。
実行計画とは?
「SQLが実行される流れ」の中で「実行計画」が出てきましたが
これは DBがSQLを「どのように処理するか」を決めた“計画書” のようなものです。
具体的には「どのインデックスを使うか」「どの順番でテーブルにアクセスするか」などが書かれています。
PostgreSQLで実行計画を確認する
では実際に実行計画を確認しましょう!
EXPLAINをクエリの最初に書いておくことで実行計画の確認ができます。
EXPLAIN SELECT * FROM users WHERE age = 25;
結果
QUERY PLAN
------------------------------------------------------------
Seq Scan on users (cost=0.00..2184.00 rows=1120 width=39)
Filter: (age = 25)
(2 rows)
EXPLAINでは実際にクエリが実行されませんがEXPLAIN ANALYZEでは実際にクエリが実行された上での実行計画が表示されます。
そのため、実際に使われたリソース(時間、行数)が表示されます。
EXPLAIN ANALYZE SELECT * FROM users WHERE age = 25;
結果
QUERY PLAN
----------------------------------------------------------------------------------------------------------
Seq Scan on users (cost=0.00..2184.00 rows=1120 width=39) (actual time=0.240..27.572 rows=1207 loops=1)
Filter: (age = 25)
Rows Removed by Filter: 98793
Planning Time: 1.108 ms
Execution Time: 27.876 ms
(5 rows)
Planning Timeとは以下のようにクエリ実行前の「計画を立てる処理」にかかった実時間
- 構文解析(パーサ)
- 実行計画の生成(オプティマイザ)
- テーブル統計情報の参照(カタログ)
Execution Timeとは実際にクエリを実行して、結果を取得し終えるまでにかかった時間です。
実行計画の見方と重要キーワード
PostgreSQLの実行計画には、よく出てくる構成やキーワードがありますので紹介します。
Seq Scan(シーケンシャルスキャン)
テーブル全体を最初から最後まで順番にスキャンする処理。
Index Scan(インデックススキャン)
インデックスを使って効率的に該当行を探す処理。
条件に合致する行だけをピンポイントで検索する。
Index Only Scan
インデックスだけで必要な情報が取得できる場合の最適なスキャン。テーブル本体を読みに行かないので高速。
Filter
スキャンしたデータのうち、WHERE句の条件で除外された内容。
'Rows Removed by Filter'
フィルタ条件によって捨てられた行数。多すぎると改善の余地あり。
loops=1
このノードが何回実行されたか。ネストしたループの中などでは増えることがある。
actual time=xxx..yyy
このノード(処理ステップの1つ)の開始〜終了までに実際にかかった時間。
actual time=0.005..1.234の場合
- 0.005 はこのノードの処理が始まった時点の累積経過時間(クエリ開始から0.005ms後)
- 1.234 はこのノードの処理が終わった時点の累積経過時間(クエリ開始から1.234ms後)
cost=xxx..yyy
コスト推定値、「この処理はどれくらい重そうか?」を見積もった値で、Optimizerの判断材料。
rows=zzz
推定されるレコード数。
まとめ
- 実行計画は「クエリがどのように処理されるか」を示す“設計図”
-
EXPLAINやEXPLAIN ANALYZEで確認できる - 代表的なスキャン方法やコスト、行数などを見ることで改善点が見えてくる
私自身まだまだ勉強中ですが、引き続き実践しながら学んだことを発信していきたいと思います!🐊
