有时我们的支持团队中的两个管理员正在尝试对 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”。
这是对的还是我误解了什么?