【テクニカル・上級編】 重量級ロック(Lock Manager) – PostgreSQL

PostgreSQLの「重量級ロック」と向き合う:その深淵とトラブルシューティングの作法

PostgreSQLのアーキテクチャを語る上で、避けて通れないのが「重量級ロック(Heavyweight Locks)」です。

「またロックの話か」と思われるかもしれません。しかし、本番環境で急にアプリケーションが沈黙したとき、あるいは特定のテーブルが突然レスポンスを返さなくなったとき、最後に頼りになるのはカタログビューと、このロックマネージャがどう動いているかという解像度です。

今日は、MVCC(多版同時実行制御)の影に隠れがちな、この「重量級ロック」という強固な守護神について、少し深いところまで掘り下げてみましょう。

—

なぜ「重量級」なのか?

PostgreSQLには大きく分けて「軽量級ロック(LWLock)」と「重量級ロック(Lock Manager)」の二種類があります。

軽量級ロックは、共有メモリ内のデータ構造(バッファキャッシュのページなど)を保護するための、いわば「CPUレベルの同期」です。一方で、今回扱う重量級ロックは、トランザクションレベルで管理されます。

なぜ「重量級」と呼ばれるのか。それは、デッドロック検出器(Deadlock Detector)による定常的な監視対象であり、`pg_locks`ビューで容易に可視化できるという「管理コスト」があるからです。これはDB全体の状態を整合的に保つための、いわば「交通整理」の役割を果たしています。

ロックマネージャの裏側:ハッシュテーブルと衝突

PostgreSQLのロックマネージャは、共有メモリ上に配置されたハッシュテーブルです。テーブルや行に対するアクセス要求が発生すると、このハッシュテーブルにエントリが登録されます。

ここで重要なのは、「ロックの競合」はハッシュテーブルのバケット数や衝突とは無関係であるという点です。競合は、あくまで「要求されたモード(Access ExclusiveやRow Shareなど)」と「既に保持されているモード」の組み合わせで決定されます。

運用でハマりやすいのが、`Access Exclusive Lock`の威力です。`ALTER TABLE`や`TRUNCATE`、あるいは`VACUUM FULL`が実行されると、この最強のロックが要求されます。これが発行された瞬間、そのオブジェクトに対する「読み取り」すら遮断されます。クエリがスタックし、バックエンドプロセスが積み上がり、PostgreSQL全体が雪崩のようにスローダウンする。この光景は、誰しも一度は経験する悪夢でしょう。

現場で「詰まった」時のデバッグ作法

本番環境で「なんだかクエリが遅いぞ」となった時、僕がまず確認するのは、教科書的なマニュアルではなく、以下のクエリです。

SELECT
pid,
mode,
granted,
relname
FROM pg_locks l
JOIN pg_class c ON l.relation = c.oid
WHERE NOT granted;

`granted = false` の行を見つける。これが、今まさにロック待ちで停止しているプロセスたちです。ここで見るべきは、「誰が待っているか」ではなく、「誰がロックを保持したまま離さないのか」という上位のトランザクションです。

パフォーマンストラブルの「よくある犯人」

1. 未コミットのトランザクション: アプリケーション側の例外処理漏れで、`BEGIN`したままコネクションが返却され、トランザクションが開きっぱなしになっているケース。
2. 長大なDDL: 巨大なテーブルへのカラム追加など、ロックの保持時間が長すぎる操作。
3. ロック待ちの連鎖: `Access Exclusive`を待っているクエリが、後ろに続く全クエリをブロックする(キューイング現象)。

これらを見抜くには、`pg_stat_activity`と`pg_locks`を突き合わせ、`backend_start`や`state_change`の時間軸を追いかける必要があります。

熟練エンジニアへのヒント:ロックの「粒度」を意識する

重量級ロックをうまく付き合うコツは、「ロックの範囲を最小化すること」に尽きます。

例えば、大量のデータを一括で更新する場合。一度に全行をロックしようとせず、適切なサイズで分割(チャンク化)し、小さなトランザクションを積み重ねる。あるいは、`SELECT … FOR UPDATE`の範囲をインデックスを使ってピンポイントに絞り込む。

PostgreSQLは非常に強力なMVCCを持っていますが、それはあくまで「読み書きの衝突」を避けるための仕組みです。論理的な整合性を担保する重量級ロックは、最後の一線を守る防波堤です。

最後に

ロックマネージャを理解することは、PostgreSQLの「呼吸」を理解することに似ています。どこで止まり、どこで流れているのか。そのリズムが見えてくると、DBは単なるデータの箱から、意思を持った生き物のように感じられるはずです。

もし今、ロックに苦しんでいるなら、まずは`pg_locks`の海を眺めてみてください。そこに、あなたのシステムが今まさに抱えている「渋滞」の正体が、必ず映し出されているはずですから。

それでは、また次回の深掘りでお会いしましょう。良いエンジニアリングを。

コメント

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