选择并锁定一行,然后更新


SELECT and lock a row and then UPDATE

我有一个从MySQL数据库中选择一行的脚本。然后更新此行。喜欢这个:

$statement = $db->prepare("SELECT id, link from persons WHERE processing = 0");
$statement->execute();
$row = $statement->fetch();
$statement = $db->prepare("UPDATE persons SET processing = 1 WHERE id = :id");
$success = $statement->execute(array(':id' => $row['id']));

该脚本同时多次调用此 php 代码。有时它会选择该行,即使它应该是"处理 = 1",因为另一个脚本在确切的时间调用它。

我怎样才能避免这种情况?

您需要做的是在此处添加某种锁,以防止像您创建的争用条件:

UPDATE persons SET processing=1 WHERE id=:id AND processing=0

这将避免双重锁定它。

要进一步改善这一点,请创建一个可用于声明的锁定列:

UPDATE persons
  SET processing=:processing_uuid
  WHERE processing IS NULL
  LIMIT 1

这需要一个VARCHAR、索引processing列,用于声明默认值为 NULL 的列。如果您在结果中修改了行,则表示您已声明记录,可以使用以下命令对其进行处理:

SELECT * FROM persons WHERE processing=:processing_uuid

每次尝试声明时,请生成一个新的声明 UUID 密钥。

尝试使用事务进行查询。在 mysql 开发站点上阅读有关它们的信息

您可以使用以下内容包装代码:

$dbh->beginTransaction();
// ... your transactions here
$dbh->commit();

您可以在此处找到文档。

使用SELECT FOR UPDATE

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

例如

SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1;

将其包装在事务中,当事务结束时,锁将被释放。