锁定行以进行两次更新


Locking row for two updates

我需要对行进行两次更新,但我需要确保它们是一起完成的,并且没有来自其他用户的其他查询可能会干扰它们。我知道SELECT...FOR UPDATE但我想在第一次更新后它当然会被解锁,这意味着有人可能会干扰第二次更新。如果其他人先更新该行,则更新将起作用,但会弄乱数据。无论如何,有没有办法确保两个更新按预期方式发生?我被告知有关事务的信息,但据我所知,它们仅适用于确保两个更新实际发生,而不是它们是否"一起"发生,除非我弄错了并且行将被锁定直到事务提交?

以下是查询:

SELECT z FROM table WHERE id='$id'

UPDATE table SET x=x+2 WHERE x>z

UPDATE table SET y=y+2 WHERE y>z

我犯了一个错误,没有提供完整的信息。那是我的错。我已经更新了查询。我遇到的问题是 z 也可以更新。如果 z 在 SELECT 之后但在其他两次更新之前更新,则数据可能会搞砸。执行事务开始/提交是否为此工作?

了解交易http://dev.mysql.com/doc/refman/5.0/en/commit.html

[... connect ...]
mysql_query("BEGIN");
$query1 = mysql_query('UPDATE table SET x=x+2 WHERE x>y');
$query2 = mysql_query('UPDATE table SET y=y+2 WHERE y>y');
if($query1 && $query2) {
    mysql_query("COMMIT");  
    echo 'Save Done. All UPDATES done.';  
} else {
    mysql_query("ROLLBACK");  
    echo 'Error Save. All UPDATES reverted, and not done.';  
}

有不同级别的事务,但基本上根据 ACID 属性,您应该期望在给定事务中,所有读取和更新都一致地执行,这意味着它将保持有效状态,但更重要的是事务是隔离的,因为在另一个事务(线程)中完成的工作不会干扰您的事务(您的选择和更新 SQL 语句分组): 这允许您广泛假设您是系统中唯一的执行线程,允许您提交该组工作(原子方式)或将其全部回滚。

每个数据库可能以不同的方式处理语义(有些可能会锁定行或列,有些可能会重新排序,有些可能会序列化),但这就是声明式数据库接口的美妙之处:您担心要完成的工作。

如前所述,在MySQL InnoDB上是事务性的,并且将支持上述内容,因此请确保您的表是使用InnoDB组织的,其他非事务引擎(例如MyISAM)不是事务性的,因此将迫使您手动管理这些事务语义(锁定)。

一种方法是锁定整个表:

LOCK TABLE `table` WRITE;
SELECT z FROM `table` WHERE id='$id';
UPDATE `table` SET x=x+2 WHERE x>z;
UPDATE `table` SET y=y+2 WHERE y>z;
UNLOCK TABLES;

这将阻止其他会话在 SELECT 和 UPDATE 期间从table表中写入和读取。

这是否是合适的解决方案取决于会话等待从表中读取或写入的合适程度。