Редактирование записей базы данных несколькими пользователями

Я разработал таблицы базы данных (нормализованные, на сервере MS SQL) и создал автономный интерфейс Windows для приложения, которое будет использоваться горсткой пользователей для добавления и редактирования информации. Позже мы добавим веб-интерфейс, который позволит осуществлять поиск по нашей производственной области.

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

  1. Ничего не делайте и надейтесь, что два пользователя никогда не будут редактировать одну и ту же запись одновременно. - Может и не случится, но что, если случится?
  2. Процедура редактирования может сохранять копию исходных данных, а также обновления, а затем сравнивать, когда пользователь закончит редактирование. Если они отличаются, покажите пользователя и подтвердите обновление - Потребуется сохранить две копии данных.
  3. Добавьте последний обновленный столбец DATETIME и проверьте его соответствие при обновлении, если нет, то покажите различия. - требуется новый столбец в каждой из соответствующих таблиц.
  4. Создайте таблицу редактирования, которая регистрирует, когда пользователи начинают редактировать запись, которая будет проверяться и не позволять другим пользователям редактировать ту же запись. - потребует тщательного обдумывания хода выполнения программы, чтобы предотвратить взаимоблокировки и блокировку записей, если пользователь выйдет из программы.

Есть ли какие-то лучшие решения, или я должен выбрать одно из них?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
29
0
24 078
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

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

Скотт Митчелл написал подробное руководство по реализации этого шаблона:
. Реализация оптимистичного параллелизма

База данных сделает это за вас. Посмотрите на «выбрать ... для обновления», который предназначен как раз для такого рода вещей. Это даст вам блокировку записи для выбранных строк, которую вы затем можете зафиксировать или откатить.

@ Марк Харрисон: SQL Server не поддерживает этот синтаксис (SELECT ... FOR UPDATE).

Эквивалент SQL Server - подсказка оператора SELECTUPDLOCK.

См. Электронная документация по SQL Server для получения дополнительной информации.

Другой вариант - проверить, что значения в изменяемой записи остаются такими же, какими они были при запуске:

SELECT 
    customer_nm,
    customer_nm AS customer_nm_orig
FROM demo_customer
WHERE customer_id = @p_customer_id

(отобразить поле customer_nm, и пользователь изменит его)

UPDATE demo_customer
SET customer_nm = @p_customer_name_new
WHERE customer_id = @p_customer_id
AND customer_name = @p_customer_nm_old

IF @@ROWCOUNT = 0
    RAISERROR( 'Update failed: Data changed' );

Вам не нужно добавлять новый столбец в таблицу (и поддерживать его в актуальном состоянии), но вам нужно создать более подробные операторы SQL и передать поля новый и Старый в хранимую процедуру.

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

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

Классический подход заключается в следующем:

  • добавить логическое поле "заблокировано" к каждой таблице.
  • по умолчанию установите значение false.
  • когда пользователь начинает редактирование, вы делаете это:

    • заблокировать строку (или всю таблицу, если вы не можете заблокировать строку)
    • отметьте флажком строку, которую хотите отредактировать
    • если флаг верен, то
      • сообщить пользователю, что он не может редактировать эту строку в данный момент
    • еще
      • установить флаг в значение true
    • отпустить замок

    • при сохранении записи установите флаг обратно в false

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

Cheung 29.11.2010 23:12

Хороший замечание, @SilverNight. Можете ли вы опубликовать правильное решение?

AJ. 30.11.2010 13:30

Для таких случаев (сбой приложения / браузера) вы можете добавить в приложение метод ForceUnlock, который принудительно установит блокировку на False.

Oleksandr 17.08.2014 09:39

Это решение с белым флагом: "разрешить одновременные модификации сложно, поэтому я собираюсь уйти".

Chris Marisic 03.10.2014 23:04

Со мной, лучший способ иметь столбец lastupdate (тип данных timetamp). при выборе и обновлении просто сравните это значение Еще одним преимуществом этого решения является то, что вы можете использовать этот столбец для отслеживания времени изменения данных. Я думаю, что это не хорошо, если вы просто создадите столбец вроде isLock для проверки обновления.

-first create filed (время обновления) для хранения последней записи обновления -когда любой пользователь выбирает запись, выберите время, сравнить между полем выбора времени и времени обновления, если (время обновления)> (время выбора), что означает, что другой пользователь обновит эту запись после выбора записи

Другие вопросы по теме