pg_stat_statements を使って遅いクエリを見つける (PostgreSQL, Ubuntu 10.04)

PostgreSQL などのリレーショナルデータベース (RDB) に対して発行されたクエリのうち、どのクエリで時間がかかっているのかを調べることは重要です。 そのための手段は色々ありますが、ここでは PostgreSQL のバージョン 8.4 以降で使用できる pg_stat_statements について、Ubuntu での使い方を説明します。

pg_stat_statements とは

pg_stat_statements とは、PostgreSQL 8.4 以降で使用できるモジュールであり、これを使うことによりサーバーで実行された全ての SQL 文の実行統計を探知することができます。

公式的な説明は以下のページにあります。

pg_stat_statements を Ubuntu 10.04 で使用する

準備

ここでは、Ubuntu 10.04 の apt-get でインストールした PostgreSQL 8.4 で pg_stat_statements モジュールを使うための手順を説明します。 PostgreSQL 8.4 は既にインストールされているものとします。 インストールされていない場合は次の apt-get コマンドでインストールしてください。

$ sudo apt-get install postgresql libpq-dev
pg_stat_statements モジュールのインストール

pg_stat_statements モジュールは postgresql-contrib パッケージに含まれていますので、まずはこのパッケージを apt-get でインストールします。

$ sudo apt-get install postgresql-contrib
pg_stat_statements モジュールを使用するように postgresql.conf を書き換える

pg_stat_statements モジュールを使用するように、/etc/postgresql/8.4/main/postgresql.conf を書き換え、shared_preload_libraries に pg_stat_statements を追加します。

# shared_preload_libraries = '' # 元々こういうコメント行があるので, 下の行を追加する
shared_preload_libraries = 'pg_stat_statements'

また、pg_stat_statements モジュールに関するいくつかの設定項目も postgresql.conf に書くことができます。 詳細は pg_stat_statements (PostgreSQL 8.4 Documentation) をご覧ください。

postgresql.conf を書き換えた後、PostgreSQL の再起動を行います。

$ sudo service postgresql-8.4 restart

これで、pg_stat_statements による SQL 文実行時の実行統計が保持されるようになります。

pg_stat_statements ビューを DB に追加

pg_stat_statements モジュールによって実行統計が保持されるようになったわけですが、実行統計を閲覧するために pg_stat_statements ビューを DB に追加する必要があります。 pg_stat_statements ビューを DB に追加するための SQL は /usr/share/postgresql/8.4/contrib/pg_stat_statements.sql というファイルで提供されていますので、このファイルの内容を SQL として実行します。

この作業は、実行統計を閲覧する対象となる各 DB でそれぞれ行わなければいけません。 ここでは、例として test_db に pg_stat_statements ビューを追加します。 なお、既に test_db という DB は作成済みであるものとします。

$ sudo -u postgres psql -d test_db -f /usr/share/postgresql/8.4/contrib/pg_stat_statements.sql

このとき, postgres ユーザーで実行するようにしなければいけないようです。

実行統計の閲覧

後は、しばらく DB を使用して統計情報を溜めて、必要に応じて pg_stat_statements ビューに対して SELECT を実行して実行統計を見てください。 calls という列はその SQL 文が発行された回数を表し、total_time という列はその SQL の実行に要した総時間です。 すなわち、1 回の実行あたりにかかった時間の平均は total_time / calls で計算できます。

その他、詳しいことは pg_stat_statements (PostgreSQL 8.4 Documentation) をご覧ください。

問題への対処

私が遭遇した問題と、その問題に対する対処法を書いておきます。

SHMMAX の設定

pg_stat_statements は別途共有メモリを必要とするため、pg_stat_statements を使用するようにすると、「システムの SHMMAX の値が小さすぎるか、PostgreSQL の shared_buffer の設定サイズが大きすぎるか、PostgreSQL の max_connections の設定値が大きすぎる」 というようなエラーが発生して PostgreSQL を起動できなくなる可能性があります。 その場合は、システムの SHMMAX の値を大きくしてやる必要があります *1

SHMMAX の値を確認するには、ipcs コマンドを使います。 また、SHMMAX の値はファイル /proc/sys/kernel/shmmax に書かれているため、このファイルの中身を見ることでも確認できます。

$ ipcs -l
  # 「共有メモリの制限」 の 「セグメントサイズの最大」 が SHMMAX の値

SHMMAX の値を一時的に設定するには、sysctl コマンドを -w オプションを付けて実行します。

$ sysctl -w kernel.shmmax=XXXXXXX
  # これで設定すると再起動で元に戻ってしまう

上の方法だとシステムを再起動すると元に戻ってしまいます。 システムを再起動しても元に戻らないようにするには、/etc/sysctl.conf ファイルに以下の行を追加します。 (XXXXXX は設定したい SHMMAX の値。)

kernel.shmmax=XXXXXX

システムを再起動するか、-i オプションを付けて sysctl コマンドを実行すればファイルに書かれた値になります。

古いバージョンだと発生するエラー

PostgreSQL 8.4.1 だと、pg_stat_statements ビューに対して SELECT を行うクエリを発行すると、2 回に 1 回ぐらいの頻度で以下のエラーが発生しました。

サーバとの接続が想定外にクローズされました
        おそらく要求の処理前または処理中にサーバが異常終了
        したことを意味しています。
サーバへの接続が切れました。リセットしています: 失敗。

これはどうやらバグのようで、PostgreSQL 8.4.7 にすると直りました。

*1:SHMMAX をそれ以上大きくできないのであれば、shared_buffer のサイズや max_connections の値を小さくする必要がある。