【実務・中級編】 重量級ロック(Lock Manager) – PostgreSQL

PostgreSQLの「重量級ロック」と上手く付き合うための実戦ガイド

やあ。データベースのチューニングやトラブルシューティングで毎日PostgreSQLと格闘しているエンジニアのみんな、お疲れ様。

今日は、PostgreSQLのアーキテクチャの中でも、特に「避けては通れないけれど、正しく理解していないと足元をすくわれる」テーマ、重量級ロック(Lock Manager)について話そうと思う。

公式ドキュメントを読むと「Lock Managerは共有メモリ上のハッシュテーブルで管理され…」なんて難しそうなことが書いてあるけど、現場の視点から言えば、これは「複数のトランザクションが同時に同じ資源に触れるときの『交通整理』」のことなんだ。

—

なぜ「重量級」と呼ばれるのか?

PostgreSQLには「軽量級ロック(LWLock)」と「重量級ロック(Heavyweight Lock)」がある。

  • 軽量級ロック: データ構造(メモリ上のページなど)の保護が目的。一瞬で終わる。
  • 重量級ロック: テーブルや行といった、トランザクションが意識するレベルのオブジェクトに対するロック。

こっちはデッドロック検出の対象になるし、トランザクションが終わるまで保持され続けるから、「重い」んだよ。ここを雑に扱うと、アプリ全体のレスポンスが急激に悪化する。「なぜか急に全クエリが詰まる」という現象の犯人は、大抵こいつだ。

—

現場でよく見る「ロックの顔ぶれ」

実務で意識すべきは、大きく分けて2種類。

1. テーブルレベルロック(ACCESS EXCLUSIVEなど)

`TRUNCATE`や`ALTER TABLE`、あるいは`VACUUM FULL`を実行したときに発生する、最強のロックだ。これが走ると、そのテーブルへの読み書きが完全にブロックされる。
「本番環境でうっかりマイグレーションを実行してサービス停止」なんて事故は、大抵これが原因だね。

2. 行レベルロック(FOR UPDATEなど)

`SELECT … FOR UPDATE` を使ったときに発生するやつだ。特定の行を「今から編集するから、他の人は触らないでね」と確保する。これ自体は粒度が細かいから、正しく使えば非常に強力な武器になる。

—

現場でのコード例:悲観的排他制御

例えば、在庫管理システムで「在庫を減らす」処理を書くとする。よくある「読み取って、計算して、書き込む」という流れをそのまま実装すると、競合が起きて在庫がマイナスになる可能性があるよね。

BEGIN;

— 行レベルのロックをかけて確保する
SELECT stock_count
FROM products
WHERE id = 123
FOR UPDATE;

— ここで計算して…
UPDATE products
SET stock_count = stock_count – 1
WHERE id = 123;

COMMIT;

この`FOR UPDATE`が重量級ロックの典型例だ。このトランザクションが`COMMIT`されるまで、他のセッションはこの行に対して`FOR UPDATE`をかけようとすると、律儀に行列に並んで待機する。

—

先輩からのアドバイス:ロック地獄を避けるために

現場でトラブルシューティングをしていると、よく「ロック待ち」でシステムが止まっている現場に出くわす。そうならないために、次の3つを心に留めておいてほしい。

1. トランザクションは「短く」保て

ロックはトランザクション終了(COMMIT/ROLLBACK)まで解放されない。
「重い計算」や「外部APIとの通信」をトランザクション内に入れてはいけない。ロックを握ったままネットワークの応答待ち…なんて状況は、DBエンジニアにとって悪夢そのものだ。

2. ロックの順序を意識せよ

デッドロックの最大の原因は、「AがテーブルXをロックしてYを待つ」「BがテーブルYをロックしてXを待つ」という相互依存だ。
アプリコードを書くときは、「常に同じ順番でリソースをロックする」というルールを徹底するだけで、デッドロックの発生率は劇的に下がる。

3. `pg_locks` を監視せよ

何かが遅いと感じたら、まずはここを見る癖をつけよう。

SELECT FROM pg_locks pl
JOIN pg_stat_activity sa ON pl.pid = sa.pid
WHERE NOT pl.granted;

これで「誰が何で待たされているのか」が一目瞭然だ。

—

最後に

重量級ロックは、決して悪者じゃない。むしろ、データの整合性を守るための「盾」だ。これを理解し、適切に制御できるようになれば、君が書くコードはより堅牢で、信頼性の高いものになる。

もし「ロックの挙動が怪しいな」と思ったら、臆せずに `pg_locks` を叩いてみてほしい。データベースの内部で何が起きているか覗くのは、案外楽しいものだよ。

さて、今日はここまで。また現場で会おう。何か分からないことがあったら、いつでも聞いてくれよな。

コメント

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