楽観的ロックと悲観的ロック
SELECT ... FOR UPDATE(悲観的ロック)と、バージョン列 / WHERE updated_at = ?(楽観的ロック)のパターンを比較します。
「楽観的ロックと悲観的ロック」はCoddyKit上の無料SQL Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSQL Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 SQL Academyコースには全4レッスンが含まれています。
2つの同時実行戦略
- 悲観的 — 読み取り時に行をロックし、他の処理から変更できないようにする
- 楽観的 — ロックせず、更新時に行が変更されていないことを確認する
悲観的:SELECT ... FOR UPDATE
今ロックし、後で書き込みます。
BEGIN;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
-- other transactions cannot lock or update this row
-- compute new balance...
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;FOR SHARE
読み取りロックです。他の処理は読み取れますが、書き込めません。
SELECT * FROM orders WHERE id = 1 FOR SHARE;
-- others can SELECT FOR SHARE but cannot UPDATE悲観的ロックの長所と短所
長所:動作を理解しやすく、リトライが不要です。
短所:同時実行性が低下し、ロック待ちやデッドロックが発生する可能性があります。
楽観的:バージョン列
バージョンとともに読み取り、WHERE version = expected を付けて書き込みます。
BEGIN;
SELECT id, balance, version FROM accounts WHERE id = 1;
-- compute new balance...
UPDATE accounts
SET balance = ?, version = version + 1
WHERE id = 1 AND version = ?;
-- check rows affected: 0 means someone else updated, retryupdated_atを使った楽観的ロック
明示的なバージョン列の代わりに updated_at を使う、同じ考え方です。
UPDATE accounts
SET balance = ?, updated_at = NOW()
WHERE id = ? AND updated_at = ?;
-- If updated_at has changed in the meantime, 0 rows affected — retry.楽観的ロックの長所と短所
長所:同時実行性が高く、待機がありません。
短所:書き込みが失敗してリトライ処理が必要になる場合があり、競合が表面化するのは UPDATE の時点だけです。
悲観的ロックを選ぶ場合
次のような場合に適しています。
- 競合の多い行で競合が頻発する短いトランザクション
- 金銭の送金 — 処理を途中まで進めたくない場合
- 競合が起きる可能性が高い長時間実行の処理
楽観的ロックを選ぶ場合
次のような場合に適しています。
- 競合がまれな、読み取り中心のワークロード
- クライアントがリクエスト間で行を保持するステートレスAPI
- モバイルやオフラインで編集してから同期する場合
ハイブリッド:FOR UPDATE NOWAIT
ロックを試み、ロック中ならすぐに失敗させて、ユーザーにリトライしてもらいます。
SELECT * FROM accounts WHERE id = 1 FOR UPDATE NOWAIT;
-- ERROR if someone else holds it — user sees a friendly retry messageアドバイザリーロック
特定の行に結び付かない、アプリケーションレベルのロックです。
SELECT pg_try_advisory_xact_lock(hashtext('order:42'));
-- True if you got the lock, false otherwise — useful for cross-row coordination.ロック対象へのインデックス付けを忘れない
WHERE 列にインデックスがない状態で FOR UPDATE を使うと、想定より多くの行がロックされる場合があります(ロックされるのは一致した行だけでなく、スキャンされた行です)。
ロックタイムアウト
lock_timeout を設定して、いつまでも待機しないようにします。
SET lock_timeout = '5s';
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- ERROR if lock not acquired in 5 secondsまとめ
悲観的ロックは行をロックし、楽観的ロックは書き込み時に確認します。
- 悲観的:FOR UPDATE — 単純ですが、同時実行性が低下する
- 楽観的:バージョン列 — より高い同時実行性が得られるが、リトライが必要
- ワークロードに応じて選び、必要に応じて組み合わせる
クイックチェック
ECサイトで在庫を減らす処理では、競合が頻発します。一般的に、どのロック戦略がより安全ですか。
よくある質問
「楽観的ロックと悲観的ロック」レッスンは無料ですか?
はい。「楽観的ロックと悲観的ロック」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、SQL Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 SQL Academyコースには全4レッスンが含まれています。
「楽観的ロックと悲観的ロック」で何を学びますか?
SELECT ... FOR UPDATE(悲観的ロック)と、バージョン列 / WHERE updated_at = ?(楽観的ロック)のパターンを比較します。 ブラウザで直接実行するハンズオンコードでSQL Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
SQL Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのSQL Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「楽観的ロックと悲観的ロック」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このSQL Academyレッスンでコードを書いて実行できますか?
はい。すべてのSQL Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。