Иногда бывает, что два администратора в нашей команде поддержки пытаются выполнить одну и ту же чувствительную операцию в строке таблицы 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 $ без предупреждения.
И делать то же, что и в решении 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; }
Создание / блокировка его во время обновления и проверка его существования / datestamp перед обновлением.
Перед обновлением:
$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 будет выполнен.
Это правильно или есть что-то, что я неправильно понимаю?
Вы можете указать старое значение в WHERE
операции UPDATE
, а затем посмотреть на количество затронутых строк:
Данный
id name amount --- ------------- --------- 1 Joe User 10
Тема 1 исполняет
UPDATE accounts SET amount=9 WHERE id=1 AND amount=10; => Query Okay, 1 row(s) affected
Тема 2 исполняет
UPDATE accounts SET amount=9 WHERE id=1 AND amount=10; => Query Okay, 0 row(s) affected
Помимо этого, я бы, скорее всего, использовал исключение раньше, назначив задачи для отдельных администраторов, чтобы уменьшить количество времени, потраченного впустую.
В вашем случае я полагаю, что блокировка – лучший подход. Вы можете использовать блокировки MySQL: GET_LOCK, RELEASE_LOCK, IS_FREE_LOCK . Сделки, на мой взгляд, не гарантируют, что строка не будет изменена, а другой процесс выполнит свою задачу по извлеченным данным.
Хотя ваш конкретный случай не имеет ничего общего с блокировкой в традиционном смысле. IMHO, вам необходимо зарегистрировать транзакции с соответствующими учетными данными и описаниями, чтобы ваши администраторы могли их прочитать и не дублировать одну и ту же модификацию баланса. Lock может защитить от одновременной модификации строки, но не от преднамеренного изменения в случае перезаписи.
Я думаю, что блокировка строк на уровне базы данных не соответствует ситуации, о которой вы говорили в первом методе. Но я не думаю, что создание файла быстрее, чем доступ к системе баз данных. Создание файла явно тяжелее CRUD в базе данных.
Итак, я предлагаю аналогичный подход с таблицей протоколирования.
pid
) Посмотрите на InnoDB и транзакции. Они более подходят для чувствительных изменений (т.е. баланса).
Базы данных, как правило, лучше, поскольку они являются централизованным решением. Если вам приходится масштабироваться из-за трафика или вообще рабочей нагрузки, синхронизировать эти файлы непросто. Если вы не ожидаете, что какая-либо потребность в масштабировании и скорости ввода-вывода будут хорошими, это нормально.
Позвольте мне упомянуть о двух возможных решениях, которые вы, возможно, упомянули выше.
Вы можете добавить «assign_id» с идентификатором вашей учетной записи администратора в сочетании с меткой времени, чтобы ваше приложение отображало предупреждение, если кто-то его редактирует.
Еще одно возможное решение – проверить, были ли сделаны какие-либо изменения во время заполнения ваших форм. Здесь можно использовать временную метку last_edited.