PostgreSQL | SQLite+MySQL経験者向け、30日で習得するPostgreSQL:設計とパフォーマンス - Day16 実行計画

SQL PostgreSQL
スポンサーリンク

Day16 前半のゴール

「“SQLがどう動いているか”を目で見て理解する感覚を持つ」

ここからは「設計とパフォーマンス」のフェーズです。
Day16 のテーマは、PostgreSQL の実行計画を見るための EXPLAIN / EXPLAIN ANALYZE

前半のゴールはこうです。
なぜ実行計画を見る必要があるのかを、感覚レベルで理解する。
EXPLAINEXPLAIN ANALYZE の違いを説明できる。
実行計画の“最低限ここだけは見る”ポイント(コスト・行数・インデックス利用)を掴む。

「SQLが遅い」ときに、闇雲に書き換えるのではなく、
“何がボトルネックか”を目で見て判断できるようになるのが、このDayの狙いです。


なぜ実行計画を見るのか

「“遅いSQL”の原因は、コードではなく“動き方”にあることが多い」

まず、根本の話からいきます。

SQLは「何をしたいか」を宣言する言語で、
「どうやって実行するか」は、DB側(PostgreSQL)が決めます。

同じSQLでも、

インデックスを使ってサクッと取る場合
全件をなめてから絞り込む場合

など、内部の“動き方”がまったく違うことがあります。

アプリ側から見ると「同じSQL」でも、
PostgreSQLの中では「全然違うルートで処理している」わけです。

だから、「SQLが遅い」ときに、

WHERE句をちょっといじる
JOINの順番を変える

といった表面的な修正だけをしても、
本質的なボトルネックに届かないことが多い。

そこで必要になるのが、「実行計画を見る」という行為です。


EXPLAIN の基本

「“このSQLをどう実行するつもりか”を教えてもらう」

EXPLAIN は、「このSQLを実行するとき、PostgreSQLはどういう手順で動くつもりか」を教えてくれるコマンドです。

例えば、こういうクエリがあるとします。

SELECT *
FROM order_items
WHERE user_id = 123
ORDER BY ordered_at DESC
LIMIT 10;
SQL

これに対して、こう書きます。

EXPLAIN
SELECT *
FROM order_items
WHERE user_id = 123
ORDER BY ordered_at DESC
LIMIT 10;
SQL

すると、PostgreSQLは「実行計画」をテキストで返してきます。
中身はこんな感じのイメージです(実際の表示は環境によって多少違います)。

Limit  (cost=0.43..15.21 rows=10 width=...)
  ->  Index Scan using idx_order_items_user_id_ordered_at
      on order_items  (cost=0.43..1200.00 rows=8000 width=...)
      Index Cond: (user_id = 123)
      Order By: ordered_at DESC

ここで重要なのは、「この時点では実際にはクエリを実行していない」ということです。
EXPLAIN は、「こう動く予定だよ」という“予告”だけを見せてくれます。


EXPLAIN ANALYZE の基本

「“実際に動かして、その結果も見せて”もらう」

EXPLAIN ANALYZE は、EXPLAIN の“本気版”です。

EXPLAIN ANALYZE
SELECT *
FROM order_items
WHERE user_id = 123
ORDER BY ordered_at DESC
LIMIT 10;
SQL

これは、実際にクエリを実行したうえで、

どのルートで処理したか(実行計画)
どれくらい時間がかかったか
何行を読み、何行を返したか

といった“実測値”を教えてくれます。

表示イメージはこうです。

Limit  (cost=0.43..15.21 rows=10 width=...)
       (actual time=0.050..0.120 rows=10 loops=1)
  ->  Index Scan using idx_order_items_user_id_ordered_at
      on order_items  (cost=0.43..1200.00 rows=8000 width=...)
       (actual time=0.050..0.110 rows=50 loops=1)
      Index Cond: (user_id = 123)
      Order By: ordered_at DESC

ここでのポイントは、

cost=... は「PostgreSQLが事前に予測したコスト」。
actual time=... rows=... は「実際に動かしてみた結果」。

という違いです。

EXPLAIN だけだと「予測」しか分からない。
EXPLAIN ANALYZE だと「予測と現実のズレ」まで分かる。

パフォーマンスチューニングを真面目にやるときは、
基本的に EXPLAIN ANALYZE を使います。


実行計画の“最低限ここだけは見る”3ポイント

「ノイズを捨てて、ボトルネック候補だけを拾う」

実行計画は情報量が多いので、
最初から全部理解しようとすると挫折します。

まずは、次の3つだけに絞って見てください。

どのノードが一番コストが高いか
どのノードが一番時間を食っているか(actual time)
インデックスを使っているか、それとも Seq Scan(全件走査)になっているか

さっきの例で言うと、

Index Scan using idx_order_items_user_id_ordered_at
→ インデックスを使っているので、良さそう。

もしこれが、

Seq Scan on order_items  (cost=...)
  Filter: (user_id = 123)

のようになっていたら、「全件走査してから user_id で絞っている」ことになります。
データ量が多いと、これが一気にボトルネックになります。

「Seq Scan なのか Index Scan なのか」を見るだけでも、
インデックス設計の問題に気づけるようになります。


例題:インデックスが効いている場合と効いていない場合

「実行計画で“インデックス設計の良し悪し”を目で見る」

同じクエリでも、インデックスの有無で実行計画が変わります。

例えば、order_items(user_id, ordered_at) にインデックスがある場合。

CREATE INDEX idx_order_items_user_id_ordered_at
ON order_items (user_id, ordered_at DESC);
SQL

この状態で EXPLAIN すると、さっきのように Index Scan が出ます。

逆に、このインデックスがない状態で EXPLAIN すると、こうなりがちです。

Limit  (cost=0.00..200.00 rows=10 width=...)
  ->  Seq Scan on order_items  (cost=0.00..20000.00 rows=10000 width=...)
      Filter: (user_id = 123)

ここでの違いは、

Index Scan
→ 条件に合う行だけをインデックスから拾ってくる。

Seq Scan
→ テーブル全件をなめて、条件に合う行だけを残す。

というところです。

実行計画を見ることで、

「このクエリはインデックスをちゃんと使っているか?」
「どのカラムにインデックスを張るべきか?」

を、感覚ではなく“証拠付き”で判断できるようになります。


実行計画を見るときの“心構え”

「怖がらずに、まずは1本のクエリから」

正直に言うと、実行計画は最初はかなり怖く見えます。
でも、Day16 前半でやりたいのは、「怖さを少し剥がすこと」です。

やることはシンプルで、

よく使う1本のSELECTを決める。
それに対して EXPLAINEXPLAIN ANALYZE を1回ずつ打ってみる。
「Seq Scan か Index Scan か」「どこが一番時間を食っているか」だけを見る。

これだけで、「SQLがどう動いているか」を“目で見る”経験が1つ積めます。

そこから少しずつ、「JOINの実行計画」「GROUP BY の実行計画」などに広げていけばいい。
いきなり全部分かろうとしなくていいので、「まずは1本のクエリと仲良くなる」と決めてしまうのがコツです。


Day16 前半のまとめ

実行計画は、「同じSQLでも、PostgreSQLが内部でどんなルートで処理しているか」を教えてくれるもので、EXPLAIN は「予測だけ」、EXPLAIN ANALYZE は「実際に動かしてみた結果(actual time・rows)」まで見せてくれる。
最低限見るべきポイントは、「Seq Scan なのか Index Scan なのか」「どのノードのコスト・実測時間が一番大きいか」で、インデックスが効いているクエリなら Index Scan using ... が出る一方、効いていないクエリは Seq Scan on ... Filter: ... のように全件走査になっていることが多い。
Day16 前半の着地点は、「よく使う1本のクエリに対して EXPLAIN / EXPLAIN ANALYZE を打ち、SQLの“動き方”を目で見てからインデックスや設計を考える」という習慣の入口に立つこと。

タイトルとURLをコピーしました