【发布时间】:2012-01-16 08:26:24
【问题描述】:
有时会发生我们支持团队中的两名管理员尝试对 db 表行执行相同的敏感操作(例如,修改行中的值)。我们需要防止这种情况。 (行锁定是不可能的,因为表是“myisam”)
我想到了几个解决方案:
在表单中设置旧值并在提交时将其与当前值进行比较
<input name="money"><input type="hidden" name="old_money" value="10">
然后在更新之前:
$currentmoney=value_from_query("select money from mytable","money");
if($currentmoney!=$_REQUEST["old_money"]){
return "value changed to $currentmoney while you were editing it, are you sure you still want to change it?!??!?!?!?";
}
else{
mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."' where user='$user_id'");
return true;
}
但可能会出现以下情况:
用户需要将货币价值从 9 美元更改为 10 美元
admin1 将他的钱改为 10 美元
用户聪明地花了 1 美元,所以他现在的钱又变成了 9 美元!
admin2 在没有任何警告的情况下将他的钱改为 10 美元。
在行中创建时间戳(updated_at 列)设置
并且与解决方案 1 中的操作相同。这样做的优势在于它说的不仅仅是简单的数据比较。我们可以肯定地说,在我们摆弄表格时数据是否发生了变化。缺点 - 除非我们将它与解决方案 1 结合使用,否则我们无法跟踪究竟是哪一列发生了更改
<input type="hidden" name="formtimestamp" value="<? echo time();?>">
然后在更新时:
$query_add = ($overriden ? "" : " and updated_at>'".securevalue($_REQUEST["formtimestamp"])."'");
if(mysql_affected_rows(mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."', updated_at=NOW() where user='$user_id' ".$query_add))==0){
return "some values were changed by someone else while you were editing it, are you sure you still want to change it?!??!?!?!?";
}
else{
return true;
}
使用特定于对象/动作的名称创建临时 0 长度文件
在更新期间创建/锁定它,并检查它的 更新前的存在/时间戳。
更新前:
$myfname="/tmp/user{$user_id}EDITMONEY.tmp";
$timedifference=((time()-filectime($myfname)); //in seconds
if(file_exists($myfname) and ($timedifference<60) and (!$overriden)){ // a minute difference
$currentmoney=value_from_query("select money from mytable","money");
return "money were edited by someone else $timedifference seconds ago and set to {$currentmoney}, are you sure you still want to change it?!??!?!?!?";
}else{
$fp = fopen("/tmp/user".intval($_REQUEST["user_id"])."EDITMONEY.tmp", "r+");
if (flock($fp, LOCK_EX)) { // do an exclusive lock
mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."' where user='$user_id'")
flock($fp, LOCK_UN); // release the lock
return true;
} else {
return "Couldn't get the lock, it's possible that someone tried to execute query simultaneously!";
}
fclose($fp);
}
目前,创建文件是我的首选方法,因为:
我认为创建本地文件比访问数据库更快。
我不需要在表中再添加一列(时间戳)
我可以轻松修改文件名以检查特定列的修改,即在mysqlupdate完成后创建文件“money_user{$userid}_modified”。
这是对的还是我误解了什么?
【问题讨论】:
-
行锁定的常用方法是在表中有两列,通常称为“lockwho”和“lockwhen” - 包含锁定行的用户的用户 ID,以及何时他们做到了。因此,当您给某人一个允许他们编辑行的页面时,请编辑带有用户 ID(用户名或用户表的更好的 FK)的“lockwho”列和过期的“lockwhen”,以防他们永远无法完成交易.然后在完成更新时,将“lockwho”设置为 NULL。如果其他人在锁定时尝试编辑该行 (
lockwho != NULL),请不要让他们。
标签: php mysql timestamp file-locking simultaneous