pg_locks ではよくわからない
PostgreSQLのロックの確認手段としては pg_locks テーブルが存在するが、このテーブルには「テーブルロック」と「未獲得の行ロック」しか含まれていない。
タプルはロック対象のオブジェクト種類ですが、行レベルロックについての情報はメモリではなく、ディスクに保存されます。 よって行レベルロックは通常、このビューには現れません。 もしプロセスが行レベルロックの待ち状態である場合は、その行ロックを保持している永続トランザクションIDを待つ状態で、そのトランザクションはビューに現れます。
未獲得状態の行ロックについても、 pg_locks では mode カラムで RowShareLock
といったざっくりとした分類しか表示されず、「FOR UPDATE」「FOR NO KEY UPDATE」「FOR SHARE」「FOR KEY SHARE」のいずれの状態でロックを取得しようとしているのかがわからない。
pgrowlocks を使う
Tatsuo Ishii さんが pgrowlocks というモジュールを提供されているようなので、こちらを使ってみると良い感じ。便利。
CREATE EXTENSION pgrowlocks;
で現在のデータベースに対して pgrowlocks 拡張を追加したら、あとはこんな感じで使える。
dbname=# SELECT * FROM pgrowlocks('tablename');
locked_row | locker | multi | xids | modes | pids
------------+--------+-------+----------+----------------+-------
(91,14) | 107127 | f | {107127} | {"For Update"} | {190}
(1 row)
具体的なレコードについては locked_row の値と該当テーブルが隠し持つシステム列 ctid カラムの一致を確認すれば良いです。したがって以下のようにコマンドを打てばOK。
SELECT * FROM tablename AS t, pgrowlocks('tablename') AS p WHERE p.locked_row = t.ctid;
各カラムの意味についてはドキュメントを参照。
よく見たらドキュメントに書かれている結果出力例が実際の出力結果と違う……。ドキュメントでは
lock_type カラムにロックモード情報が含まれるように書いているが、上記の通り modes カラムで返ってくる(PostgreSQL 9.3あたりのLockモード追加されたことにあわせて仕様変わったのにドキュメント更新が漏れているだけだと思う)。
補足
- pgrowlocks で取得されるのは獲得済みの行ロックのみのようなので、獲得待ち状態の行ロックについては pg_locks で確認する。