環境
Mysql8.0
Laravel10.3.2
プロジェクトの状況
飲食店の予約システムの予約機能。
座席に対して予約をする仕組み。
座席の使える時間がある(seat.start_time, seat.end_time)
予約のリクエストは座席が使える時間のどこでもできるような仕様。
やってみた1 楽観的ロック(Optimistic Lock)
楽観的ロックはDBにロックをかけない方法なので、楽観的ロックと呼ばれたらしい。
楽観的ロックはversionを管理することでダブルブッキングを解決する。
僕のプロジェクトの場合、座席テーブルにversionカラムを持たせて
transactionが開始したところのversionとupdateが行われる時のversionを比較、
versionが変わっていたら、処理を行わない。
プロジェクトの仕様上、座席テーブルのupdateは予約時間が被らなくても変わるので、楽観的ロックは却下。
やってみた2 悲観的ロック(Passimistic Lock)
DBにロックをかけるから悲観的ロックと呼ばれたらしい。
悲観的ロックは
予約可能な座席をSelectしたところで、そのレコードにロックをかける。(update for)
例えば、transactionAとtransactionBがほぼ同時に行われたとしよう。
transactionAが終了するまで待って、transactionBのSelectが行われたはずなのに、
なんで、Aの結果が反映されてないのか???????
ここで僕は詰まってしまった。
トランザクション分離レベルについて調べた
Mysqlのデフォルト設定はREPEATABLE READだ。
REPEATABLE READは一つのドランザクション内で同じ結果を保証するが、新しいレコードが追加される場合不整合が生じる。
transaction分離レベルをREAD COMMITTEDに変更してみた。
READ COMMITTEDはコミットされたデータに対してSelectできる。
つまり、BのSelectにAの結果が反映される。
これでダブルブッキングは起こらなくなった。
問題点
座席をSelectしているので、選択した予約時間と被らない時間を Selectしても
座席のSelectができない。
このように違う時間帯のSelectをしていても
先にSelectをしたAの方の予約処理が終わるまで
Bの処理は待機することになる。
予約できる時間を自由に設定できるようにするためには
このような仕組みになるしかなかった。
座席に対して予約できる時間を1時間毎に固定するしようだったら
この問題もなかっただろう、、