2013-03-08 45 views
1

我的理解是,Apache为每个传入请求创建一个单独的PHP进程。这意味着,如果我有代码,不会是这样的:如果记录存在 Apache,MySQL和PHP竞争条件

  • 如果记录不存在,创建它
  • 然后,这是容易受到竞争条件

    1. 检查,是它不是?如果两个请求同时进入,并且它们同时触发(1),它们都会返回错误,然后都会尝试插入新记录。

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

    +0

    INSERT IGNORE,LOCK TABLE或在不想复制的列上使用UNIQUE索引都是很好的选择。 – 2013-03-08 01:08:34

    +0

    另请参阅http://stackoverflow.com/questions/1361340/how-to-insert-if-not-exists-in-mysql – 2013-03-08 01:10:17

    +0

    'SELECT ...FOR UPDATE' – 2013-03-08 01:23:52

    回答

    2

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

    0

    我认为在第二步处理错误应该是足够的。如果两个进程尝试创建一条记录,那么只要你已经适当地配置了MySQL表格,其中一个就会失败。在正确的字段中使用UNIQUE是实现这一诀窍的一种方法。

    0

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

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

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

    另请注意,MySQL尚未支持嵌套事务: 您不能在事务内部拥有事务,因为第一次提交将提交所有内容。

    +0

    +1对于线程的区别 – landons 2013-03-08 01:31:04

    +3

    这是一个简单的竞态条件,并且与死锁没有任何关系 – 2013-03-08 01:38:22

    +3

    使用事务(单独)不会解决问题,因为两个事务都可以看到该记录还不存在。 – 2013-03-08 01:47:50

    1

    老实说,我很少遇到这种情况。通常可以通过重新评估业务需求来缓解这种情况。即使两个不同的用户试图插入完全相同的数据,我也会推迟重复管理用户,而不是应用程序。但是,如果有理由在应用程序逻辑中强制执行唯一约束,那么我会使用INSERT IGNORE... ON DUPLICATE KEY UPDATE...查询(当然在表中具有相应的UNIQUE索引)。