使用以下代码来防止竞争条件是否安全? (key
和status
字段和mysql_affected_rows用于实现锁定)
$mres = mysql_query("SELECT `values`, `key`, `status`
FROM `test`
WHERE `id` = 1");
$row = mysql_fetch_array($mres);
if($row['status'] != UPDATING) {
$mres = mysql_query("UPDATE `test` SET
`status` = UPDATING,
`key` = `key` + 1
WHERE `id` = 1 AND `key` = ".$row['key']);
if($mres && mysql_affected_rows()) {
//update here safely and then...
mysql_query("UPDATE `test` SET
`status` = NOT_UPDATING,
`key` = `key` + 1
WHERE `id` = 1");
}
}
我的测试表明它不安全,或者我应该在我的代码中搜索隐藏得很好的错误。 表是MyISAM
答案 0 :(得分:0)
在检索值之前,您应首先“获取锁定”。否则他们可能会在你获得锁定之前改变。
$mres = mysql_query("UPDATE `test` SET
`status` = 'UPDATING'
WHERE `id` = 1 AND `status` = 'NOT_UPDATING'");
if ($mres && mysql_affected_rows()) {
// got the lock
// now select and update
}
id
最好是数据库中的唯一字段,否则事情可能会非常奇怪'UPDATING'
和'NOT_UPDATING'
在您的代码中$row['status']
UPDATING
是否有一个有意义的值(如果它是假/ null?)
答案 1 :(得分:0)
您可以在MySql中检查GET_LOCK
和RELEASE_LOCK
函数以模拟行锁。
http://dev.mysql.com/doc/refman/5.1/en/miscellaneous-functions.html#function_get-lock
使用此方法,您无需更新行。如果出现问题,还可以使用mysql_affected_rows()
,您可以使用始终锁定的行完成(例如,如果您在释放行之前脚本崩溃,则将其状态更新为NOT_UPDATING)。授予GET_LOCK
的锁定在连接终止时自动释放。