【问题标题】:Race conditions with Apache, MySQL and PHPApache、MySQL 和 PHP 的竞争条件
【发布时间】:2013-03-08 01:01:33
【问题描述】:

我的理解是 Apache 为每个传入的请求创建一个单独的 PHP 进程。这意味着,如果我有类似的代码:

  1. 检查记录是否存在
  2. 如果记录不存在,则创建它

那么这很容易受到竞争条件的影响,不是吗?如果两个请求同时进入,并且它们都同时命中 (1),它们都会返回 false,然后都尝试插入新记录。

如果是这样,人们如何处理这个问题?围绕这 2 个请求创建一个 MySQL 事务会解决这个问题,还是我们需要做一个全表锁?

【问题讨论】:

标签: php mysql apache


【解决方案1】:

据我所知,您无法创建跨不同连接的事务。也许一种解决方案是将您正在检查的列设置为唯一的。这样,如果有两个连接到10,并且10 不存在。他们都会尝试创建10。一个会先完成插入行,一切都很好;那么后面一秒钟的连接将失败,因为该列不是唯一的。如果您捕获到抛出的异常,那么您随后可以SELECT 数据库中的记录。

【讨论】:

    【解决方案2】:

    老实说,我很少遇到这种情况。通常可以通过重新评估业务需求来缓解这种情况。即使两个不同的用户试图插入完全相同的数据,我也会推迟对用户重复的管理,而不是应用程序。

    但是,如果有理由在应用程序逻辑中强制执行唯一约束,我会使用 INSERT IGNORE... ON DUPLICATE KEY UPDATE... 查询(当然,在表中使用相应的 UNIQUE 索引)。

    【讨论】:

      【解决方案3】:

      我认为在第二步处理错误应该就足够了。如果两个进程尝试创建一条记录,那么只要您正确配置了 MySQL 表,其中一个就会失败。在正确的字段中使用 UNIQUE 是解决问题的一种方法。

      【讨论】:

        【解决方案4】:

        Apache 不会“为每个传入请求创建单独的 PHP 进程”。 它要么使用进程池(默认,prefork 模式),要么使用线程。

        正如您所提到的,竞争条件也可能被称为(或导致)数据库“死锁”。 @见what is deadlock in a database?

        在需要的地方使用事务应该可以解决这个问题,是的。
        通过确保检查记录是否存在并在事务中创建它,整个操作是原子的。
        因此,其他请求不会尝试创建重复记录(或者,根据实际查询,创建不一致或进入实际死锁)。

        另请注意,MySQL 不(尚)支持嵌套事务: 事务中不能有事务,因为第一次提交将提交所有内容。

        【讨论】:

        • 这是一个简单的竞争条件,与死锁无关
        • 使用事务(单独)并不能解决问题,因为两个事务都可以看到该记录尚不存在。
        • @Karoly Horvath:是的,为了更清晰而进行了编辑。该示例不会导致死锁,但一般来说,竞争条件可能会促进它们,显然取决于实际的数据库/代码。
        • 不,它仍然完全具有误导性......在许多层面上。谁赞成这个?好吧,我看到其中一个...:D
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-05-03
        • 2013-01-14
        • 2020-07-12
        • 2016-03-21
        • 1970-01-01
        • 2015-05-16
        • 2017-01-23
        相关资源
        最近更新 更多