【问题标题】:How do I make sure I'm not updating the same record from multiple processes? Do I need table locks?如何确保我没有从多个进程更新同一记录?我需要桌子锁吗?
【发布时间】:2014-05-31 15:00:09
【问题描述】:

我正在开发一个使用 MySQL 数据库作为后端的项目(从 PHP 访问)。有时,我选择一行,对其进行一些操作,然后更新数据库中的记录。

我担心另一个用户可能会在第一次选择后立即在同一行上启动类似的过程,并且他的更改可能会覆盖第一个用户所做的一些更改(因为第二个用户的选择尚未包含这些更改)。

这是一个实际问题吗?我应该锁定表吗?这不会严重影响我的应用程序的性能吗?还有其他解决方案吗?

为了全面了解我的信息,我还运行了一些 CRON 作业,这些作业也可能在修改相同的数据。

谢谢!

【问题讨论】:

  • 使用事务,确保您使用的是符合 ACID 的引擎
  • 感谢您的建议。所以基本上,我只是将引擎设置为 InnoDB,然后在我选择之前执行“START TRANSACTION”,然后在我更新之后执行“COMMIT”?还有什么我需要考虑的吗,或者是这样吗?
  • 如果您在事务中更新多个表。尝试在事务中以相同的顺序访问您正在更新的表。这减少了死锁的机会。
  • 解决行在您检查时可能被其他用户更新的问题。考虑在事务开始时对您可能更改的行执行“选择更新”。即使您不更新它们,您也可以确定在使用它们时它们不会被更改。

标签: php mysql sql table-locking table-lock


【解决方案1】:

除了显式使用事务之外,我可以想到两种解决方案:

  • 使用 SELECT .. 进行更新:http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
  • 手动更改一个值,这样该行就不会被其他查询选中:

    SET @update_id := 0;

    UPDATE table_name SET status = 'IN_PROCESS', id = (SELECT @update_id := id) WHERE status = 'WAITING' AND [your condition] LIMIT 1;

    选择@update_id;

此处,要选择的行的值必须为 status="WAITING"。并且当这个查询运行时,它会选择 ID,并改变 'status' 的值,所以该行不能被其他查询选择。

【讨论】:

  • 我认为“选择...进行更新”仅在事务中才有意义,因为资源被释放?
  • 关于您的第二种方法,想象一下同时发生 10 次更新。你怎么知道谁拥有该行?我建议“更新”不是事务或其他“锁定”策略之外的“原子”。
  • 是的,'Select .. for update' 应该在事务中,或者“auto_commit=0”,隐式启动会导致它启动事务。
  • 至于第二点,UPDATE 是原子的。当有 10 个连接请求一行时,获取 'first' 行的连接 'status="WAITING"',并在它释放行上的锁之前将其更改为 'IN_PROCESS',因此下一个请求可以' t 得到这个确切的行,因为“状态”字段不满足条件。
  • 这似乎是一个可以接受的好答案。我还有一个问题:当我使用 SELECT FOR UPDATE 时,尝试访问/修改该行的其他进程到底会发生什么?他们会等到它们可用,还是会失败?谢谢!
猜你喜欢
  • 2015-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
相关资源
最近更新 更多