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

e37o9pze  于 2023-03-07  发布在  PHP
关注(0)|答案(2)|浏览(135)

我正在建立一个酒店风格的预订系统,我失去了方向。我已经在准确的预订时刻卡住了,当添加预订,我担心未来的情况。
我的流程非常简单,它允许多个用户对同一个房间进行预订,直到其中一个用户在其他用户之前按下预订按钮。预订信息保存在会话中,不会存储在数据库中,直到有人到达最后一步并按下"预订"。在每一步中,系统都会检查房间是否可用,如果不可用,则会显示错误页面。
现在的问题是防止竞态条件结合到最后一个强制检查,如果仍然有足够的空间剩余:
当用户在最后一步,他/她选择付款方式,并按下预订按钮。检查后,如果选择的付款选项是确定的(表格数据没有被更改),它重定向到预订控制器,其中:
预约控制器

<?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保存预订,#__booking_room保存所选房间的信息(如avg_price、房间数等),它有一个外键指向#__rooms,另一个外键指向#__bookings
我在担心什么?
1-在上次可用性检查期间,我正在使用锁定的两个表之外的其他表,并向我显示错误。
2-当第二个用户的执行到达表锁定点时,是否等待他们获得自由或引发错误/异常?我可以捕获异常并将用户重定向到错误页面,但锁表并不意味着保留,它可能在保存信息时发生任何问题。
我希望我的工作流程是确定的,请让我知道,如果我是好去或我应该改善我的代码。
谢谢
编辑:
值得一提的是,这里的商业理念就像Expedia,booking等等,你可以选择更多的房间类型和每次预订的单位。事实上,我的房间表不是针对特定的/真实的房间,而是针对如果还没有预订的话,每天有固定数量的单位可用的房间类型。

xiozqbni

xiozqbni1#

我的方法会更简单,在存储实际预订的表中,我会为预订的特定房间设置一个外键,并为字段roomID, date设置一个UNIQUE索引,这将确保同一个房间ID不会在同一天被记录两次。
接下来,当客户确认预订时,我将像您现在所做的那样运行事务中的所有内容。当代码到达最后一个位置并尝试插入新预订时,如果在另一个客户预订该房间之前的片刻,DB将不允许您插入该房间在该日期的另一个预订。此时,我将:

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

lsmepo6l2#

我不能说你的工作流程又名业务领域是正确的选择,因为它取决于你的业务。但从技术上讲,你想有一个预订事务,也提供了对重复预订的检查。最简单的方法可能是一个房间预订日期表与房间是和日期的唯一约束。因此,在预订事务期间,您还将每天的房间ID插入到表中,唯一约束将确保第二次尝试失败。
PS:如果一个表被锁定,PHP只会等待解锁。不会抛出异常(至少如果连接在x秒后没有关闭)

相关问题