はじめに
この記事では
- なぜPostgreSQLを監視するのか??
- PostgreSQLのどこをどう監視するのか??
についてまとめました。
なぜ監視するのか??
監視していなければ異常発生時に初動が遅れます。
例えばスロークエリ2sの閾値で監視設定をしておき、
それを超えた際、該当のクエリをアラート通知できていれば、
ユーザからのクレームが来る前に、
検知して対応することもできるかもしれません。
また、
正常時と異常時とを比較でき、
問題特定がしやすくなります。
例えば、昨日まで何の問題もなく動作していたのに、
急に今日特定の機能がスロークエリ連発する。
正常時の実行計画や統計情報と
異常時のそれらを比較したところ
今日統計情報が大きく変わり、それに伴って実行計画も変わっていた。
など、正常時と何が変わっているかをまず見ることで、
どのように対処すれば良いかの特定が容易になります。
何をどう監視するか
監視にはサーバの監視とPostgreSQLの監視の大きく2つがあります。
死活監視
サーバの死活監視では、
pingコマンドを利用してサーバと通信が行えることを
確認します。
一方、PostgreSQLの死活監視では、
PostgreSQLのプロセスが正常に起動していることを
pg_isreadyコマンドで確認します。
$ pg_isready -h IPアドレス
戻り値が0であれば正常に動作しています。
さらに、正常にクエリを受け付けられる状態であることを
簡単なクエリを発行して確認します。
$ 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ビュー
このビューでは以下が確認できます。
- コミット/ロールバックの回数
- データベース単位のキャッシュヒット率
- デッドロック発生回数
-- 確認用クエリ
SELECT * FROM pg_stat_database WHERE datname = 'データベース名'
-- xact_commit:コミット回数
-- xact_rollback:ロールバック回数(PostgreSQL内部で発行されるSQLもカウントされる)
-- blks_read:共有バッファ以外からデータを読み取った回数(ディスクI/O発生)
-- blks_hit:共有バッファからデータを読み取った回数
-- deadlocks:デッドロック発生回数
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ビュー
動作中のバックエンドプロセスの情報が確認できます。
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―設計・運用計画の鉄則