我想创建一个如下所示的函数:
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