【ひかんてきろっく】
悲観的ロック とは?
最終更新:
💡 先にロックを取り、競合する処理を待たせる方式
競合を見込んで処理の前にロックを取得し、同じ対象への競合する変更などを待たせる方式。在庫確認と更新などを一つのトランザクションで保護するために使う。守れる範囲はロックの種類と処理の設計による。
📌 このページのポイント
- 確認と変更に先立って、対象のロックを取得する
- PostgreSQLではSELECT ... FOR UPDATEで取得した行をロックできる
- 同じトランザクション内で、取得した状態の確認から更新まで行う
- 待ち時間やデッドロックを考え、処理を短くしてエラーを扱う
悲観的ロックって具体的にどう書くの?
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)
💬 「衝突するかもしれない」と悲観的に考え、事前にロックをかけて防ぐ方式