【ひかんてきろっく】

悲観的ロック とは?

最終更新:
💡 先にロックを取り、競合する処理を待たせる方式

競合を見込んで処理の前にロックを取得し、同じ対象への競合する変更などを待たせる方式。在庫確認と更新などを一つのトランザクションで保護するために使う。守れる範囲はロックの種類と処理の設計による。

📌 このページのポイント
先にロックを取り、競合する処理を待たせる 在庫1の行を、2処理が確保する例 処理A 処理B ロック取得 在庫1を読む 在庫を0へ 更新 COMMIT 解放 Aがロックを保持 同じ行の ロックを要求 取得を待つ 取得して 在庫0を読む 時間の経過 Bは在庫を確認し、販売を見送る
PostgreSQLのREAD COMMITTEDで、各処理がトランザクション内でSELECT ... FOR UPDATEを使う仮の例。Aは確認・更新後にCOMMITし、Bは待った後の在庫を確認する。横の矢印は時間、縦の点線はAの解放時点。通常のSELECTまで全部止める図ではない。分離レベルやDBの仕様によって動作は変わる。
ひよこ ひよこ
悲観的ロックって具体的にどう書くの?
ペンギン先生 ペンギン先生
PostgreSQLの例なら、BEGINで開始したトランザクション内でSELECT * FROM products WHERE id = 1 FOR UPDATEを実行するよ。取得した行への他のトランザクションの更新や競合するロックは、通常は終了まで待つ。普通のSELECTまで全部止めるわけではないんだ。
ひよこ ひよこ
どんな場面で使うの?
ペンギン先生 ペンギン先生
READ COMMITTEDの例なら、在庫の確認と減算をまとめて行いたいときなどだよ。ロックを取った後に在庫を確認し、足りれば減らしてCOMMITする。後からロックを取った処理も、その時点の在庫を確認する必要がある。読むだけでトランザクションを終えたり、ロック前の古い値で判断したりすると保護にならないんだ。
ひよこ ひよこ
ロックしている間、他のリクエストはどうなるの?
ペンギン先生 ペンギン先生
競合する処理は待つことがあるよ。PostgreSQLではNOWAITで行のロックをすぐ取れなければエラーにできるし、SKIP LOCKEDで取れない行を飛ばせる。ただし飛ばした結果は全行を含まないので、在庫がないと判断する一般的な検索へそのまま使うものではない。用途とエラー時の動作を決めよう。
ひよこ ひよこ
デッドロックってどういう状態?
ペンギン先生 ペンギン先生
Aが行Xを持ってYを待ち、BがYを持ってXを待つような、互いに進めない状態だよ。PostgreSQLは検出すると一方のトランザクションを中止する。複数対象のロック順序をそろえ、トランザクションを短く保とう。再試行するなら、確認と更新を含むトランザクション全体をやり直すんだ。
ひよこ ひよこ
ロックを使えば整合性は確実なの?
ペンギン先生 ペンギン先生
ロックは取得した対象と競合する処理を制御する仕組みだよ。業務上の条件の確認や、必要な行を漏れなく保護する設計は別に必要なんだ。競合が多い処理で役立つことがある一方、待ち時間も増える。分離レベルやDBの仕様も確認して選ぼう。
ペンギン
まとめ:ざっくりこれだけ覚えればOK!
「悲観的ロック」って出てきたら「先にロックを取り、競合する変更を待たせる方式」と思えばだいたいOK!
📖 おまけ:英語の意味
「Pessimistic Locking」 = 悲観的な(Pessimistic)ロック(Locking)
💬 「衝突するかもしれない」と悲観的に考え、事前にロックをかけて防ぐ方式

参考資料

← 用語集にもどる