###前提・実現したいこと
php5.6とpostgresql9.4でバックのレンタルサービスサイトを構築しています。
バックの重複予約が発生しないように、排他制御を実装したいのですが
どのように実装するのが良いのか分からないので質問しました。
###テーブル構造
tbl_rentalはお客様の情報を、tbl_scheduleはレンタルスケジュールを管理するテーブルで
下記のような構造になっています。
■tbl_rental
カラム名 | 型 | 列3 |
---|---|---|
rental_id | integer | レンタルID |
name | text | お客様名 |
payment | integer | 支払額 |
■tbl_schedule
カラム名 | 型 | 列3 |
---|---|---|
rental_id | integer | レンタルID |
bag_id | integer | レンタルするバックID |
rental_from | date | レンタル開始日 |
rental_to | date | レンタル終了日 |
ここで、下記のような不正なデータが作られないように制御を実装したいと考えています。
rental_id | bag_id | rental_from | rental_to |
---|---|---|---|
1 | 10 | 2017-11-13 | 2017-11-15 |
2 | 10 | 2017-11-14 | 2017-11-16 |
###実現方法
####方法1(LOCK TABLE)
あるトランザクションがLOCK TABLEでロックを取得すると、他トランザクションはロックが開放されるまでブロックされるので
排他制御を実現できる。しかし、テーブル全体がロックされるため予約処理とは関係ない処理(管理画面など)にも影響がでる。
$rental_id = レンタルID; $bag_id = バッグID; $rental_from = レンタル開始日; $rental_to = レンタル終了日; BEGIN; LOCK TABLE tbl_schedule IN ACCESS EXCLUSIVE MODE; $rental = SELECT rental_id FROM tbl_rental WHERE bag_id = $bag_id AND rental_from <= $rental_to AND rental_to >= $rental_from; if (empty($rental)) { INSERT INTO tbl_schedule (rental_id, bag_id, rental_from, rental_to) VALUES ($rental_id, $bag_id, $rental_from, $rantal_to); COMMIT; } else { print '既に同じ期間に別のレンタル予約が入っています。'; ROLLBACK; }
####方法2(SELECT FOR UPDATE)
行ロックなのでテーブルロックに比べてパフォーマンスが良い。
しかしSELECTの結果が空の場合だと他トランザクションがブロックされないため、タイミングによっては重複予約データが作られてしまう。
$rental_id = レンタルID; $bag_id = バッグID; $rental_from = レンタル開始日; $rental_to = レンタル終了日; BEGIN; $rental = SELECT rental_id FROM tbl_rental WHERE bag_id = $bag_id AND rental_from <= $rental_to AND rental_to >= $rental_from FOR UPDATE; if (empty($rental)) { INSERT INTO tbl_schedule (rental_id, bag_id, rental_from, rental_to) VALUES ($rental_id, $bag_id, $rental_from, $rantal_to); COMMIT; } else { print '既に同じ期間に別のレンタル予約が入っています。'; ROLLBACK; }
####方法3(ロック用テーブル)
あるトランザクションがLOCK TABLEでロックを取得すると、他トランザクションはロックが開放されるまでブロックされるので
排他制御を実現できる。方法1とは異なり、ロック専用のテーブル(tbl_lock)をロックするので、予約処理以外の処理には影響がない。
$rental_id = レンタルID; $bag_id = バッグID; $rental_from = レンタル開始日; $rental_to = レンタル終了日; BEGIN; LOCK TABLE tbl_lock IN ACCESS EXCLUSIVE MODE; $rental = SELECT rental_id FROM tbl_rental WHERE bag_id = $bag_id AND rental_from <= $rental_to AND rental_to >= $rental_from; if (empty($rental)) { INSERT INTO tbl_schedule (rental_id, bag_id, rental_from, rental_to) VALUES ($rental_id, $bag_id, $rental_from, $rantal_to); COMMIT; } else { print '既に同じ期間に別のレンタル予約が入っています。'; ROLLBACK; }
上記3つの方法が思いついたのですが、他に良い方法がありましたらご教授いただけると助かります。
何卒よろしくお願い致します。

