Иногда бывает, что два администратора в нашей группе поддержки пытаются выполнить одну и ту же чувствительную операцию над строкой таблицы БД (скажем, изменяя значение в строке).Мы должны предотвратить это.(Блокировка строки невозможна, потому что таблицы «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);
}
Для файла сейчассоздание - мой предпочтительный подход, потому что:
Я думаю, что создавать локальный файл быстрее, чем базу данных доступа.
Мне не нужно добавлятьеще один столбец (отметка времени) в таблице
Я могу легко изменить имя файла, чтобы проверить наличие конкретной модификации столбца, т.е. создать файл "money_user {$ userid} _modified", когда mysqlupdate будет выполнен.
Это правильно или я что-то не так понял?