【はいたせいぎょ】
排他制御 とは?
最終更新:
💡 同じデータの更新を、順番に進めるための交通整理
同じデータや資源を複数の処理が扱うとき、競合する操作を同時に進めないようにする制御。データベースでは更新用のロックなどを使い、二重予約や更新の上書きを防ぐ。
📌 このページのポイント
残り1枚のチケットを、2人で同時に買ったら?
2人とも「1枚ある」と読んだあと、別々に予約を確定すると、二重販売が起きる可能性があるね。確認と更新を一つの取引として扱い、競合する処理を調整する必要があるんだ。ロックを使うなら、関係する処理が同じルールを守ることも大切だよ。
ロックは、いつ取ればいいの?
悲観的ロックの例では、残数を確かめる前に更新用のロックを取り、残数の確認と予約を進めるよ。PostgreSQLのSELECT FOR UPDATEで同じ行を処理する場合、別の更新用ロックの取得は待たされるんだ。先の取引が終わったら、後の処理は残数を確認し直して、売り切れなら予約しないようにするよ。
楽観的ロックなら、待たなくていいの?
最初から更新用のロックを保持し続ける代わりに、更新時に版番号などを条件へ入れて競合を検知する考え方だよ。「読んだときの版と同じ場合だけ更新する」を一つの更新操作で行うんだ。先に別の処理が更新して条件に合わなくなったら、読み直すか、利用者へ競合を伝える必要があるね。
デッドロックって、どんな状態?
Aが資源Xを押さえてYを待ち、BがYを押さえてXを待つように、互いの解放待ちになる状態だよ。PostgreSQLは検知すると一方の取引を中止して解消するんだ。ロックを取る順番をそろえる、取引を短くする、必要に応じて取引全体を再試行する、といった対策を考えよう。
どちらの方式を使えばいい?
競合の起き方や、待てる時間、失敗したときの再試行を含めて決めよう。サービスの種類だけで一律には選べないよ。ロックで待たせる範囲を広くしすぎず、整合性を守る条件と、処理の流れを一緒に設計するのが大切なんだ。
もっと詳しく知りたい人へ
SELECT FOR UPDATEを使うと、普通の読み取りも止まる?
PostgreSQLの行ロックでは、普通のSELECTによる読み取りは止まらないよ。同じ行への更新・削除や、競合する行ロックの取得が待たされるんだ。図のBも、単に読もうとしているのではなく、同じ行の更新用ロックを取ろうとして待っているよ。読み取りでどの値が見えるかは、取引の分離レベルなどにも関係するんだ。
まとめ:ざっくりこれだけ覚えればOK!
「排他制御」って出てきたら「同じデータを同時に書き換えて困らないようにする交通整理」と思えばだいたいOK!
📖 おまけ:英語の意味
「Exclusive Control」 = 排他的な制御
💬 ある処理が資源を扱っている間、競合する処理を同時に進めないようにする考え方だよ。何を待たせるかは、使うロックの種類や仕組みによって違うんだ