楽観ロック・悲観ロック らっかんろっく・ひかんろっく
同時実行制御バージョン番号排他制御トランザクション競合検出ORM
楽観ロック・悲観ロックについて教えて
簡単に言うとこんな感じ!
「楽観ロック」は「どうせ競合しないでしょ」って信じて後から確認する方式、「悲観ロック」は「絶対競合する!」って先に鍵をかける方式だよ! 楽観は速いけど競合したらやり直し、悲観は確実だけど他を待たせる。競合がまれなら楽観、頻繁なら悲観を選ぶんだ!
楽観ロック・悲観ロックとは
**楽観ロック(Optimistic Locking)と悲観ロック(Pessimistic Locking)**は、複数のトランザクションが同じデータを同時に更新しようとした際の「競合をどうさばくか」という2つの戦略です。
楽観ロックは「競合はめったに起きないだろう」という前提で、読み取り時にはロックをかけず、書き込み時に「自分が読んだ後にデータが変わっていないか」をバージョン番号やタイムスタンプで確認します。変わっていなければコミット、変わっていたら競合エラーとしてアプリケーション側でリトライします。ロック待ち時間がないため高いスループットを実現できます。
悲観ロックは「競合が起きるに決まっている」という前提で、読み取り段階からロックをかけて他のトランザクションを完全にブロックします。競合は確実に防げますが、ロック待ちが発生してスループットが下がる可能性があります。
2つの方式の比較
| 比較項目 | 楽観ロック | 悲観ロック |
|---|---|---|
| ロックタイミング | 書き込み確認時(後) | 読み取り時(前) |
| 競合の検出方法 | バージョン番号 / タイムスタンプ | DB レベルのロック(SELECT FOR UPDATE) |
| 競合時の挙動 | エラーを返し、アプリがリトライ | ロック解放まで待機 |
| デッドロックの可能性 | ほぼなし | あり |
| 向いているケース | 読み取りが多い、競合がまれ | 書き込みが多い、競合が頻繁 |
| 実装場所 | 主にアプリケーション層 | DB の排他ロック機能 |
| 代表的な用途 | WebアプリのCMS・在庫確認 | 座席・チケット予約、金融取引 |
楽観ロックの実装例
-- 楽観ロックの UPDATE 例(バージョン番号で競合検出)
UPDATE products
SET stock = stock - 1,
version = version + 1
WHERE id = 123
AND version = 3; -- 読み取り時のバージョンと一致するか確認
-- 更新行数が0なら競合発生 → アプリ側でリトライ
歴史と背景
- 1970年代後半:楽観的同時実行制御の理論がH.T. Kung・John T. Robinsonらの研究で発表
- 1980年代:商用DBMSが行レベル悲観ロックを実装。OLTPの標準的手法として普及
- 2000年代:Ruby on Rails(ActiveRecord)が楽観ロック(
lock_versionカラム)をORM標準機能として採用したことで広く認知される - 現在:Hibernate・Django ORM・TypeORMなど主要ORMが楽観ロックを標準サポート。REST APIのETagヘッダーも楽観ロックの一形態として広く使われている
関連する規格・RFC
| 規格 | 内容 |
|---|---|
| RFC 7232 (HTTP) | ETagとIf-Match / If-None-Match ヘッダーを用いたHTTPレベルの楽観的同時実行制御 |
| SQL標準 | SELECT … FOR UPDATE による悲観ロック取得構文 |