如何避免SQL 1出现竞争条件.选择,2<;改变值>;,3.更新


How do I avoid a race condition with SQL 1. SELECT, 2. <change value>, 3. UPDATE?

我想创建一个如下所示的函数:

function addValue($id, $new_value) {
    // Get the value from the database.
    $value = `SELECT value FROM table WHERE id = $id`
    // Somehow transform the value.
    $value .= $new_value
    // Put it back in the database.
    `UPDATE value SET value = $new_value WHERE id = $id`
}

问题是,如果另一个线程同时调用addValue(),它可能会在第一个线程UPDATE之前选择旧值,从而在第二个线程使用旧值UPDATE时丢失该值。

我应该把桌子锁成下面的样子吗?

function addValue($id, $new_value) {
    // lock()?
    // Get the value from the database.
    $value = `SELECT value FROM table WHERE id = $id`
    // Somehow transform the value.
    $value .= $new_value
    // Put it back in the database.
    `UPDATE value SET value = $new_value WHERE id = $id`
    // unlock()?
}

您需要使用事务并获取将要更新的行的写锁(SELECT ... FOR UPDATE)。

SET AUTOCOMMIT=0
SELECT value FROM table WHERE id = $id FOR UPDATE
...
UPDATE table SET value = $new_value WHERE id = $id
COMMIT
SET AUTOCOMMIT=1 
...

此外,这只适用于事务存储引擎(InnoDB)。

您的问题引出了两个要点:

  • 确保数据库操作之间的数据完整性是通过事务来实现的,事务可以通过不同的数据库层驱动程序(如Mysqli和PDO)来实现
  • 如果您需要在更新值之前提取该值,那么更新可能应该是相对的,因此它应该相对应用于现有字段,从而避免对事务的需要。例如,如果运行UPDATE table SET value = $increase + value WHERE id = $id;,则可以定义该值要增加10