在酒店预订系统PHP / MySQL中防止竞争条件和锁定表

时间:2017-08-13 18:15:25

标签: php mysql mysqli race-condition table-locking

我正在建立一个酒店风格的预订系统,我正在失去方向。我已经卡在准确的预订时刻,加上预订,我担心会出现问题。

我的流量非常简单,它让多个用户可以在同一个房间进行预订,直到其中一个用户按下预订按钮。预订信息保存在会话中,并且永远不会存储在数据库中,直到有人到达最后一步并按“预订”。在每个步骤中,系统检查房间是否可用,如果不是,它会给出错误页面。

现在问题是如果仍然有足够的房间剩下足够的房间,就会阻止竞争条件和最后一次强制检查:

当用户处于最后一步时,他/她选择付款方式并按书本按钮。在检查所选付款选项是否正常(表单数据未被更改)后,它会重定向到预订控制器,其中:

预订控制器

<?php
ReservationController extends JControllerLegacy{

public function placeReservation(){

    //Do some checks to prevent direct access, booking existence and user completed all steps

    $reservatioModel = $this ->getModel('Reservation')
    if(!$reservationModel->placeReservation()){
         //set errors and redirect to error page
         return false;
    }

    //booking had been placed, passes the data to payment plugin
}

?>

预订模式:

 <?php
 ReservationModel extends JModelLegacy{

public function placeReservation(){

    //Get all variables and objects

    //Lock the tables to prevent any insert while the last check
    $db ->lockTable(#__bookings);
    $db ->lockTable(#__booking_room);

    //Perform the last mandatory check with tables locked to prevent read the table right one moment before another transaction insert the booking and allowing overbooking. THIS CHECK MUST BE 100% SURE NO ONE ELSE IS MANIPULATING/READING THE TABLE
    if(!$this ->checkRoomsAvailability($data))
        return false;

    try{
        $db ->transactionStart();

        //Add the reservation in the booking table
        if(!$this ->_saveBooking()){
           $db ->rollBack();
           return false;
        }

        //Add the room/s to the middle table #__booking_room
        if(!$this -> _saveRooms())
           $db ->rollBack();
           return false;
        }

        $db ->transactionCommit();

    }catch(Exception $e){

        $db ->rollBack();
        $this ->setError('We were not able to complete your reservation.');
        return false;
    }

    $db ->unlockTables();

}
 ?>

上面提到的表是InnoDB,#__ bookings持有预订,#__ book_room持有所选房间的信息(如avg_price,房间数等),它有#__rooms的外键和一到#__预订< / p>

我关心的是什么?

1 - 我在最后一次可用性检查中使用的是其他表而不是两个锁定并且给我错误。

2 - 当第二个用户的执行到达表锁定点时,是否等到它们空闲或者是否引发错误/异常?我可以捕获异常并将用户重定向到错误页面,但是locket表并不意味着保留了预留,在保存信息时可能会出现任何问题。

我希望我的工作流程没问题,如果我好好去,或者我应该改进我的代码,请告诉我。

由于

编辑:

值得一提的是,商业理念就像是Expedia,预订等,您可以在这里预订每个预订的房间类型和单位。事实上,我的房间桌不适合特定/真实房间,但如果还没有预订,则每天都有固定数量的房间。

2 个答案:

答案 0 :(得分:0)

我的方法会更简单。在存储实际预留的表中,我将为保留的特定房间设置外键,并为字段UNIQUE提供roomID, date索引。这将确保同一房间ID在同一日期不能被记录两次。

接下来,当客户确认预订时,我会在您正在进行的交易中运行所有内容。当代码到达最后一个地方并尝试插入新预订时,如果在另一个客户预留该房间之前的那一刻,数据库将不允许您为该日期插入该房间的另一个预订。那时我会:

  • 回滚并抛出错误,或
  • 如果还有另一个同类型的房间仍然可用,请再试一次另一个房间(有自己的roomID

答案 1 :(得分:0)

我不能说您的工作流程(业务领域)是否是正确的选择,因为它取决于您的业务。但从技术上讲,您希望进行预订交易,同时提供双重预订的支票。最简单的方法可能是房间预订日期表,对房间和日期有独特的约束。因此,在您预订交易期间,您还会将每天的房间ID插入到表格中,唯一约束将确保第二次尝试失败。

PS:如果表被锁定,PHP将等待解锁。不会抛出任何异常(至少如果连接在x秒后没有关闭)