Состояние гонки между выбором и обновлением - PullRequest
4 голосов
/ 14 декабря 2011

Мне нужно обновить строку в базе данных Oracle таким образом, чтобы я не скрывал изменения, сделанные другим клиентом, в моем веб-приложении.

В моей текущей системе я выполняю следующее:

SELECT * FROM table WHERE id=:ID AND lastmodified=:LASTMOD

если строка все еще существует с той же датой последнего изменения, когда мы начали, мы знаем, что никто не изменил ее, поэтому мы можем обновить, используя время последнего изменения.

Однако, делая это вручную, используя два сеанса, я заметил, что если два клиента выбирают примерно в одно и то же время, один из них может пропустить, что строка была изменена между этапами выбора и обновления из-за того, что оба произошли в течение одной секунды или миллисекунды .

Конечным результатом является то, что я блокирую изменения другого пользователя, и нет никаких предупреждений о том, что это произошло.

Я думал об использовании SELECT FROM UPDATE, но, видимо, это плохая идея (особенно для веб-приложений), статья рекомендует перечитать (что я и делаю выше), но я все еще думаю У меня есть риск забега.

Редактировать: Ясно дал понять, что меня беспокоит способ указания времени.

Ответы [ 4 ]

5 голосов
/ 14 декабря 2011

Я предполагаю, что само ваше утверждение UPDATE проверяет значение lastmodified, которое вы прочитали в своем выражении SELECT, как предполагает ninesided.

Если lastmodified - это DATE, то существует потенциальное состояние гонки, если в одну и ту же секунду происходит несколько обновлений одной и той же строки, поскольку DATE имеет гранулярность только для второй.Если lastmodified представляет собой TIMESTAMP, то, с другой стороны, окно, в котором может возникнуть условие гонки, является гораздо более ограниченным, поскольку TIMESTAMP будет иметь от 3 до 9 цифр с точностью до секунды (3 в большинстве Windowsмашин, 6 на большинстве машин Unix).Весьма маловероятно, но не невозможно, чтобы у вас было два обновления в одну и ту же миллисекунду или даже одну и ту же микросекунду.Но это не безошибочно.

Вы можете использовать значение, сгенерированное последовательностью, вместо даты последнего изменения.Это может гарантировать, что вы не потеряете обновление, поскольку последовательность NOCYCLE не будет возвращать одно и то же значение дважды.Но если вы пойдете по этому пути, вы либо потеряете информационное преимущество, связанное с наличием даты последнего обновления в каждой строке, либо сохраните несколько дополнительных байтов данных в каждой строке таблицы.Любой из этих компромиссов может стоить того, в зависимости от вашего приложения, или может создать больше проблем, чем решить.

1 голос
/ 14 декабря 2011

Один из подходов, который вы можете использовать, заключается в том, что когда пользователь обновляет таблицу, он добавляет

AND lastmodified = :LASTMOD

в предложение WHERE оператора обновления, где :LASTMOD - это значение, возвращаемое исходным выбором пользователя. Если обновление коснулось теперь строк (SQL%ROWCOUNT=0), то вы знаете, что второй пользователь обновил эту строку, так как первый пользователь первоначально выполнил свой выбор.

0 голосов
/ 15 декабря 2011

Другим вариантом было бы включить ROWDEPENDENCIES для таблицы (что требует перестройки таблицы!) И использовать псевдостолбец ORA_ROWSCN.Отслеживание SCN на уровне строк и блоков стоит 6 байт на строку;однако он меньше столбца DATE или TIMESTAMP и не требует создания дополнительных объектов - например, последовательности или триггера, чтобы убедиться, что последовательность заполнена.

Для получения дополнительной информации,см. вопрос Атома Тома Кайта здесь .

0 голосов
/ 14 декабря 2011

Я не уверен насчет внутреннего решения SQL, но я предполагаю, что вы используете какой-то язык сценариев (php? Perl?) Для выполнения веб-приложений / бэкэнда, поэтому вы можете просто использовать их для блокировки файлов или семафор / мьютекс, чтобы заблокировать запись и ожидание (если заблокировано), или просто попросить пользователя подождать минуту и ​​повторить попытку.

В качестве альтернативы вы можете взглянуть на источник MediaWiki. Я знаю, что у них есть защита, позволяющая двум пользователям не перезаписывать содержимое друг друга в БД. И, честно говоря, я действительно считаю, что Википедия была бы довольно большим примером платформы, где такое одновременное редактирование может происходить не раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...