監視する〜PostgreSQLのきほん〜

はじめに

この記事では

  • なぜPostgreSQLを監視するのか??
  • PostgreSQLのどこをどう監視するのか??

についてまとめました。


なぜ監視するのか??

監視していなければ異常発生時に初動が遅れます。

例えばスロークエリ2sの閾値で監視設定をしておき、

それを超えた際、該当のクエリをアラート通知できていれば、

ユーザからのクレームが来る前に、

検知して対応することもできるかもしれません。


また、

正常時と異常時とを比較でき、

問題特定がしやすくなります。

例えば、昨日まで何の問題もなく動作していたのに、

急に今日特定の機能がスロークエリ連発する。


正常時の実行計画や統計情報と

異常時のそれらを比較したところ

今日統計情報が大きく変わり、それに伴って実行計画も変わっていた。

など、正常時と何が変わっているかをまず見ることで、

どのように対処すれば良いかの特定が容易になります。


何をどう監視するか

監視にはサーバの監視とPostgreSQLの監視の大きく2つがあります。


死活監視

サーバの死活監視では、

pingコマンドを利用してサーバと通信が行えることを

確認します。


一方、PostgreSQLの死活監視では、

PostgreSQLのプロセスが正常に起動していることを

pg_isreadyコマンドで確認します。

pg_isreadyコマンドでプロセスの正常動作確認
$ pg_isready -h IPアドレス

戻り値が0であれば正常に動作しています。


さらに、正常にクエリを受け付けられる状態であることを

簡単なクエリを発行して確認します。

SQLが正常に実行できることを簡単なクエリで確認
$ psql postgres -c "SELECT 1"


正常動作の監視

想定する性能でサービス提供できていることが正常動作とみなし、

PostgreSQLに想定どおりの負荷がかかっており、

サーバのリソースを枯渇させずに処理できることが確認ポイントです。


まず、サーバの正常動作の監視としては、

リソースの使用状況を確認します。

OS付属のvmstatコマンドで大まかに確認して

詳細をnetstat、iostat、sarコマンドで確認する流れです。


vmstatコマンド

procsで待ちになっているプロセスの数(bの数)

cpuの状況

などから大まかな状況を把握できる


netstatコマンド

各バックエンドプロセスのTCP/IP接続の利用状況を確認する

StateがTIME_OUTとなっているTCP/IP接続がないか

Recv-Q、Send-Qが想定以上の値になっているTCP/IP接続がないか

の観点で確認する


iostatコマンド

I/Oに関する情報を確認する。

デバイスごとに秒間のI/O回数(tps)

読み込み量(kB_read/s)

書き込み量(kB_wrtn/s)

を確認する


sarコマンド

CPUの状況確認は-uオプションを指定する。

複数のコアを持つサーバの場合コアごとに状況を確認できる

I/Oの状況確認は-dオプションを指定する。

平均リクエストサイズ(avgrq-sz)、平均キューサイズ(avgqu-sz)、待ち時間(await)など

各デバイスの具体的な利用状況を確認できる。

ネットワークの状況確認は-nオプションを指定する。

秒間の受信パケット数(rxpck/s)や受信バイト数(rxkB/s)、送信パケット数(txpck/s)や送信バイト数(txkB/s)

が確認できる。


最近だとクラウドサービスのManagedなデータベースを利用するケースが

多いと思うので個人的には、サーバの監視はそこまで覚えなくても

ポータルからUIで簡単に確認や監視できるので問題ないと思います。


しかし、PostgreSQLの正常動作の監視は

理解して個別にきちんと設定する必要があると思います。


PostgreSQLのビューを確認すれば、

PostgreSQLに想定どおりの負荷がかかっていることを確認できます。

それぞれのビューで何が監視できるか確認していきましょう。


pg_stat_databaseビュー

このビューでは以下が確認できます。

  • コミット/ロールバックの回数
  • データベース単位のキャッシュヒット率
  • デッドロック発生回数
pg_stat_databaseビューの確認例
-- 確認用クエリ
SELECT * FROM pg_stat_database WHERE datname = 'データベース名'

-- xact_commit:コミット回数
-- xact_rollback:ロールバック回数(PostgreSQL内部で発行されるSQLもカウントされる)
-- blks_read:共有バッファ以外からデータを読み取った回数(ディスクI/O発生)
-- blks_hit:共有バッファからデータを読み取った回数
-- deadlocks:デッドロック発生回数


pg_stat_user_tablesビュー

このビューでは各テーブルに対する処理の概要が確認できます。

pg_stat_user_tablesビューの確認例
-- 確認用クエリ
SELECT * FROM pg_stat_user_tables WHERE relname = 'テーブル名'

-- seq_scan:シーケンシャルスキャン回数
-- seq_tup_read:シーケンシャルスキャンで取得した合計の行数
-- n_tup_ins、n_tup_upd、n_tup_del、n_tup_hot_upd:「挿入」「更新」「削除」「HOT更新」の回数
-- n_live_tup、n_dead_tup:「有効」「無効」行数
-- last_autovacuum、last_autoannalyze:で直近の「自動バキューム」、「自動アナライズ」実行時刻


pg_statio_user_tables/pg_statio_user_indexesビュー

このビューを利用すれば

テーブル・インデックス単位でキャッシュヒット率が求められます。

テーブルのキャッシュヒット率算出例
SELECT (heap_blks_hit * 100.0)/(heap_blks_hit + heap_blks_read) FROM pg_statio_user_tables WHERE relname = 'テーブル名'


pg_stat_activityビュー

動作中のバックエンドプロセスの情報が確認できます。

pg_stat_activityビュー確認例
SELECT
    datname, -- データベース名
    pid,     -- プロセスID
    usename, -- ユーザー名
    application_name, -- アプリケーション名
    client_addr, -- クライアントのアドレス
    client_port, -- クライアントのポート
    backend_start, -- バックエンドプロセスの開始時間
    xact_start, -- トランザクションが開始した時刻
    query_start, -- 現在実行中のクエリの開始時間
    state,   -- プロセスの状態
    wait_event_type, -- 処理待ちの状態
    wait_event, -- 具体的に何を待っているのか
    query    -- 実行中のクエリ
FROM
    pg_stat_activity;


pg_locksビュー

ロック待ちを起こしているプロセスを確認できます。

ロックを発生させているプロセス特定クエリ例
SELECT
    lock_waiting.pid AS waiting_pid,-- ロック待ちをしているプロセスのID。
    lock_waiting.mode AS waiting_mode,-- ロック待ちをしているプロセスが要求しているロックのモード。
    lock_waiting.locktype AS waiting_locktype,-- ロック待ちをしているプロセスが待機しているロックのタイプ。
    lock_holding.pid AS holding_pid,-- ロックを保持しているプロセスのID。
    lock_holding.mode AS holding_mode,-- ロックを保持しているプロセスが保持しているロックのモード。
    lock_holding.locktype AS holding_locktype,-- ロックを保持しているプロセスが保持しているロックのタイプ。
    waiting_activity.query AS waiting_query,-- ロック待ちをしているプロセスが実行しているクエリ。
    holding_activity.query AS holding_query,-- ロックを保持しているプロセスが実行しているクエリ。
    pg_class.relname AS locked_relation-- ロックされているリレーション(テーブル)の名前。
FROM
    pg_locks lock_waiting
JOIN
    pg_stat_activity waiting_activity ON waiting_activity.pid = lock_waiting.pid
JOIN
    pg_locks lock_holding ON lock_waiting.locktype = lock_holding.locktype
    AND lock_waiting.database = lock_holding.database
    AND lock_waiting.relation = lock_holding.relation
    AND lock_waiting.page = lock_holding.page
    AND lock_waiting.tuple = lock_holding.tuple
    AND lock_waiting.virtualxid = lock_holding.virtualxid
    AND lock_waiting.transactionid = lock_holding.transactionid
    AND lock_waiting.classid = lock_holding.classid
    AND lock_waiting.objid = lock_holding.objid
    AND lock_waiting.objsubid = lock_holding.objsubid
    AND lock_waiting.pid != lock_holding.pid
JOIN
    pg_stat_activity holding_activity ON holding_activity.pid = lock_holding.pid
LEFT JOIN
    pg_class ON lock_waiting.relation = pg_class.oid
WHERE
    lock_waiting.granted = FALSE;

pg_stat_activityビューやpg_classシステムカタログと結合して必要な情報を取得するのが良い。


ロックの情報を取得するには、

postgresql.confファイルの[log_lock_waits]をonにする必要があります。


その他の監視

その他のよくある監視としてはスロークエリの監視があります。

postgresql.confのlog_min_duration_statmentで設定した閾値(ミリ秒)を超えた場合、

ログ出力させてアラート通知などができます。


今回のテーマについては以上です。

現場で役立つデータベースの基礎知識という

まとめ記事を作ったので是非ご覧ください。

初めてデータベースを触る方に向けて〜新人プログラマー時代の自分に伝えたいこと〜


参考文献

第14章 死活監視と正常動作の監視

[改訂3版]内部構造から学ぶPostgreSQL―設計・運用計画の鉄則

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください