【実務・中級編】 ANALYZEコマンド – PostgreSQL

「なぜかクエリが遅い」を解決する、PostgreSQLの『ANALYZE』との付き合い方

現場でデータベースを触っていると、必ず一度はぶち当たる壁があります。「さっきまで爆速だったクエリが、急に重くなった」という現象です。

原因の多くは、PostgreSQLのクエリプランナが「データの中身」を誤解していることにあります。そんなとき、魔法の杖のように効くのが `ANALYZE` コマンドです。今日は、教科書には載っていない「現場でのANALYZEの勘所」について、少し話をさせてください。

そもそも、PostgreSQLは「勘」で動いている

まず前提として、PostgreSQLのプランナは全知全能ではありません。彼らは `pg_statistic` という「統計情報」という名のカンニングペーパーを見ながら、「どのインデックスを使って、どの順番でテーブルを結合するか」を考えています。

もし、このカンニングペーパーが古かったらどうなるでしょうか?

  • 「このテーブルには100件しかデータがないはずだ」と信じ込んでいるのに、実際には1,000万件に増えている。
  • データの分布が偏っているのに、均等に散らばっていると誤解している。

結果として、プランナは「全テーブルスキャンで十分だろ」という、最悪の実行計画を選んでしまうわけです。これを正すのが `ANALYZE` の役割です。

ANALYZEを打つべき「タイミング」

「じゃあ、毎日cronで全テーブルANALYZEすればいいのでは?」と思うかもしれません。でも、それはおすすめしません。`ANALYZE` はテーブルをスキャンするコストがかかるため、頻繁にやりすぎるとDBに余計な負荷をかけてしまいます。

僕が現場で `ANALYZE` を手動で叩くのは、主に以下のようなケースです。

1. バッチ処理で大量データを投入した直後

  • 100万件単位のINSERTやDELETEをした後は、統計情報が激しくズレます。バッチの最後に「お疲れ様」のつもりで `ANALYZE` を叩くのが鉄則です。

2. 実行計画が明らかに不自然なとき

  • `EXPLAIN ANALYZE` を叩いてみて、「Actual Rows(実際の行数)」と「Estimated Rows(予測行数)」が桁違いにズレているなら、迷わず実行です。

3. WHERE句の条件が特定のデータに偏ったとき

  • 特定のステータスだけ異常に多い、といったデータ分布の変化があったときも有効です。

具体的な使い方とコード例

使い方は非常にシンプルですが、現場では範囲を絞ることが重要です。

1. テーブル全体を更新する

一番基本ですね。対象テーブルを指定するだけです。

ANALYZE users;

2. 特定の列だけに絞って更新する

特定の列にインデックスを貼った直後や、その列の検索条件が頻繁に変わる場合は、列指定で負荷を下げられます。

ANALYZE users (last_login_at, status);

3. バッチ処理の最後に入れる例

例えば、PythonやGoのバッチ内でトランザクションを閉じた直後にこう書くのが、僕の定番スタイルです。

— 大量更新処理
BEGIN;
DELETE FROM logs WHERE created_at < '2023-01-01'; COMMIT; -- 統計をリフレッシュしてプランナを正気に戻す ANALYZE logs;

ここがプロのポイント:オートバキュームを信じすぎない

「PostgreSQLにはオートバキューム(autovacuum)があるから大丈夫じゃないの?」という声も聞こえてきそうです。確かにその通り。バックグラウンドで勝手に `ANALYZE` も走っています。

でも、オートバキュームが走るまで待てない時があるんです。

例えば、深夜のバッチ処理で巨大なテーブルを掃除した直後、朝一番のユーザーアクセスが急増する時間帯に「統計が古いまま」だと、朝のラッシュでDBが悲鳴を上げます。「統計情報を最新に保つのは、運用側の責任」という意識を持つことが、エンジニアとしてワンランク上に行くコツですよ。

まとめ:迷ったらEXPLAINを見てみよう

結局のところ、`ANALYZE` は「プランナに現実を見せるためのコマンド」です。

もしクエリが遅いと感じたら、まずは `EXPLAIN ANALYZE` で実行計画を確認してください。そこで予測と現実に大きな乖離があれば、それは `ANALYZE` が必要なサインです。

怖がらずに叩いてみてください。適切に統計情報を更新してあげるだけで、クエリは驚くほど軽快に走るようになりますから。

それでは、また現場でお会いしましょう!

コメント

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