データベース操作・制御

楽観ロック・悲観ロック らっかんろっく・ひかんろっく

同時実行制御バージョン番号排他制御トランザクション競合検出ORM
楽観ロック・悲観ロックについて教えて

簡単に言うとこんな感じ!

「楽観ロック」は「どうせ競合しないでしょ」って信じて後から確認する方式、「悲観ロック」は「絶対競合する!」って先に鍵をかける方式だよ! 楽観は速いけど競合したらやり直し、悲観は確実だけど他を待たせる。競合がまれなら楽観、頻繁なら悲観を選ぶんだ!


楽観ロック・悲観ロックとは

**楽観ロック(Optimistic Locking)悲観ロック(Pessimistic Locking)**は、複数のトランザクションが同じデータを同時に更新しようとした際の「競合をどうさばくか」という2つの戦略です。

楽観ロックは「競合はめったに起きないだろう」という前提で、読み取り時にはロックをかけず、書き込み時に「自分が読んだ後にデータが変わっていないか」をバージョン番号やタイムスタンプで確認します。変わっていなければコミット、変わっていたら競合エラーとしてアプリケーション側でリトライします。ロック待ち時間がないため高いスループットを実現できます。

悲観ロックは「競合が起きるに決まっている」という前提で、読み取り段階からロックをかけて他のトランザクションを完全にブロックします。競合は確実に防げますが、ロック待ちが発生してスループットが下がる可能性があります。


2つの方式の比較

比較項目楽観ロック悲観ロック
ロックタイミング書き込み確認時(後)読み取り時(前)
競合の検出方法バージョン番号 / タイムスタンプDB レベルのロック(SELECT FOR UPDATE)
競合時の挙動エラーを返し、アプリがリトライロック解放まで待機
デッドロックの可能性ほぼなしあり
向いているケース読み取りが多い、競合がまれ書き込みが多い、競合が頻繁
実装場所主にアプリケーション層DB の排他ロック機能
代表的な用途WebアプリのCMS・在庫確認座席・チケット予約、金融取引

楽観ロックの実装例

楽観ロックの動き ① 読み取り(version=3) ① 読み取り(version=3) ② 処理・変更準備 ② 処理・変更準備 ③ version=3 のまま→ UPDATE成功!version→4 ③ version=3 のはずが→ すでに4!競合エラー トランザクションA トランザクションB B はリトライ → 再読み取り(version=4) アプリ層でリトライロジックを実装する必要がある
-- 楽観ロックの 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 による悲観ロック取得構文

関連用語