【问题标题】:php race condition database with row entries带有行条目的php竞争条件数据库
【发布时间】:2018-03-27 14:15:21
【问题描述】:

所以我试图弄清楚如何避免竞争条件。 我有一个产品,您可以在其中预订有限的资源。

每个资源都有一定数量的容量。容量是根据名为 booking_resources 的数据库表中的行数来计算的,其中每个预订的资源都有一行。

在代码执行期间,我会进行多项检查,例如:

  • 此人是否有权访问
  • 资源在给定时间范围内是否可用
  • 预订选项是否有效
  • 进行预订

“进行预订”时,预订所需的资源被插入到 booking_resources 表中。这使得具有 resource_id 的行数增加,从而使下一次检查资源在给定时间范围内可用。 我的问题是我找不到任何关于在这种情况下该怎么做的文档。 我应该在我的资源表中添加一个额外的列并以这种方式锁定该行吗? (我使用 innodb

我读过一些关于使用 select ... for update.. 的内容,但这在我的情况下没有意义,因为我没有“缓存”该行中的预订数量。 整个想法是我有一个 24/7 可用的资源,每次预订都会占用它的一些容量。它的容量可能为 10(这意味着我可以让 10 个人同时使用该资源)。这也意味着(理论上)人们对资源的预订可以重叠,只要不超过 10 人同时使用它。

希望我的问题能说明我面临的问题,并希望你们有一个优雅的解决方案来防止我的应用程序超额预订。

数据结构:

预订:

标识 | company_id |积分 |来自 |到 |价格 |预订数据 |预订者选择 | due_order_id_datetime(如果不是订单的一部分,则删除预订时)|创建 |更新 |已删除

booking_resources:

标识 |预订ID |资源 ID |来自 |到 |积分 |创建 |更新 |已删除

资源:

标识 | company_id |姓名 | min_booking |最大预订量 | min_points |最大点数 |创建 |更新 |已删除

为了能够重现该问题,我制作了一个 javascript,可在两个不同的窗口中同时单击同一个按钮。这是通过使用 settimeout 计时器和点击事件(使用 jquery)来完成的。

<script>
function syncClick(selector,time_to_activate){
    date = new Date();
    if(date.getTime() >= time_to_activate){
        console.log('clicked');
        jQuery(selector).click();
    } else {
        setTimeout(function(){
            console.log("diff:"+(date.getTime() - time_to_activate));
            syncClick(selector,time_to_activate);
        },1);
    }
}
</script>

谢谢。 /W

【问题讨论】:

  • 你在使用事务吗?
  • 嗯......不......但这只是因为我看不到交易在这里会有什么帮助。他们真的锁定了桌子本身吗?所以一次只能插入一件东西?我是否必须进行插入,然后“检查”它是否仍然存在,如果没有,则将其中一项事务“回滚”?
  • 好吧,如果您将多个数据库添加/更新/删除操作作为一个序列的一部分,那么建议您在语句失败或无法执行时防止数据不一致(例如,因为它未通过测试,例如可用的东西)。目前尚不清楚这是否是您的场景的一部分,但听起来可能是这样。它可能不是您问题的解决方案,但它应该在其他方面有所帮助。
  • 无论如何,你还没有真正给我们一个真正清晰的情况。我们看不到您的实际数据结构或您如何计算容量。但理想情况下,如果有人询问特定时间段,您或许应该暂时将其锁定,直到他们确认。并给他们一个时间限制,直到他们的独家访问结束。很多票务网站都会为热门活动这样做——您选择多张票,然后您有几分钟时间确认预订并付款,然后再重新向公众发布,您可能会丢失它。跨度>
  • 我会在一秒钟内评论你写的所有东西。只是为了给一个清晰的图像。 “this”实际上是时间范围的锁定。创建预订后,您有 X 分钟的时间确认(创建订单 - 并付款)..

标签: php mysql race-condition


【解决方案1】:

所以对于以下情况:

UserA 和 UserB 在星期一检查 BookA 的可用性。所以在他们都检查后它会显示“可用”。使用检查可用性的存储过程“sp_CheckAvail”。在这两种情况下它都应该返回 true。

现在用户 A 请求预订星期一,所以点击“预订”。这本书将保留在星期一。

请求应如下所示:

call sp_CheckAvail(bookid, $date, available); //This function will check 
                                              //wheter the book is available at the moment
                                              of the request

if available
    BEGIN
      insert into reservations(bookid, date)
      values($book, $date);  //This will write the reservation only if the book is available
    END

select available as result;

当然,这必须全部在事务中运行,因此即使在同一时刻点击链接,也只有一个客户会获得预订。

请理解我无法为您编写完整的查询。但是这样就可以了。

【讨论】:

  • 我不明白反对意见。如果有办法对日期范围进行数据库约束,我更愿意并建议这样做。但是因为他需要检查那个范围,所以最好这样。对于分布式应用程序,重要的是在做某事之前再次签入事务。
猜你喜欢
  • 2012-04-08
  • 1970-01-01
  • 2020-11-22
  • 2013-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-08
相关资源
最近更新 更多