ロックとは
ファイルへの書き込みやデータベースの更新などの際、データの整合性を守るために他のプロセスからの 同時アクセスを一時的に禁止する仕組み
本ページでは主にRDBを念頭に置いて話を進めます。
ロックの種類
- 悲観ロック:真のロック
- 楽観ロック:本質的にはロックではないかもしれない
ロックの使いどころ
同時に1人しか更新できない画面
- 画面表示時に対象レコードのロックを獲得
- 獲得できない場合は「他の人が編集中です」などと表示して戻る
- 画面から離れるときにロックをリリース
悲観ロック
- 1つのリソースは1つの「ロック」を持つ
- ロックを獲得したプロセスしか処理を行えない
- ロック獲得に失敗したプロセスの挙動は2種類
- 取れるまで待つ
- 失敗する
RDBの場合、レコードのロック、テーブルのロックなどが存在します。
データベースロックが発生することも・・・恐ろしい・・・
悲観ロックとWeb
- Webアプリは、終了をサーバー側で捕捉できない
- このことから、リクエストを跨いだロック操作は厳禁
ユーザーが離脱したらロックがいつまで経ってもリリースされなくなる!
これらのことから、Webアプリと悲観ロックは相性が悪い。
そこで楽観ロック
- 本質的にはロックじゃない
- レコードにバージョンを持っておく
- 更新時にバージョンをインクリメント
- 画面に、操作中のレコードのバージョンを覚えておく
- 画面から更新するとき、覚えておいたバージョンと一致するときにみ、更新する
- 実装としては
where version = ?とする
- 実装としては
- 一致しない場合はロック獲得失敗と扱う
RDBの楽観ロックサポート
- SQL ServerのRowVersion型
- UPDATEすると自動でインクリメントされる
- Entity Frameworkでは
byte[]にするのがポイント
意外と、同等の機能を持つRDBMSは少ないようなので、SQL Server系を使うときは活用したいところ
設計上のポイント
- 入れ子になっているデータ
- 子だけ更新する際に、バージョンはどれを参照するのか?
- 業務仕様によっては、親リソースのバージョンを見る必要がある
Webアプリでも悲観ロックは必要
- リクエストを跨いでロック保持するのがNGなだけで、同一リクエストの中では適切にロックを取得する必要がある
- UNIQUE制約を確実に避けながらINSERTする、など
最後に
- Webアプリは本質的にマルチプロセス
- 操作を跨ぐロックは楽観ロックを使う
- 楽観・悲観のどちらか?ではない。適切に組み合わせる
- ロックを後から設計・実装するのは超大変
- 最初から考えておきましょう!