2011-12-14 5 views
4

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

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

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

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

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

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

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

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

+1

Я думаю, что статья, на которую вы ссылаетесь, весьма сомнительна. В жизни DBA гораздо более жестокие баны, чем SELECT FOR UPDATE. Например, процессы, которые выполняют gazillions ненужных чтений и выполняют дополнительные задания вместо правильной стратегии блокировки. – APC

+0

Реальная проблема с SELECT FOR UPDATE заключается в том, что она * stateful *, тогда как HTTP - * без гражданства *. Использование технологии, предназначенной для чтения документов для реализации приложений OLTP, является безумным и источником бесконечной печали, но, видимо, мы являемся индустрией мазохистов 8-) – APC

+0

Я также читал http://broadh2o.net/docs/database /oracle/oracleLocks.html во время исследования этого вопроса, я был обеспокоен тем, как блокировка может застрять даже после того, как вы отключите «... завершение процесса не всегда приводит к блокировкам. Отключение рабочей станции до того, как вы вернетесь домой, не всегда выпускает блокировки Замки освобождаются только тогда, когда изменения совершаются или откатываются ». Поэтому я был обеспокоен тем, что произойдет, если я сделаю выбор для обновления, а затем не откат (хотя, я думаю, это все равно было бы риском, если бы у меня было незафиксированное обновление, так как это также заблокировало строку)? – TownCube

ответ

5

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

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

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

+0

Я никогда не был большим поклонником «маловероятного, хотя и невозможного», последовательности! Спасибо Джастину. – TownCube

1

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

AND lastmodified = :LASTMOD 

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

0

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

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

0

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

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

Смежные вопросы