Нарушение ограничения первичного ключа, несколько пользователей - PullRequest
1 голос
/ 12 июня 2010

Допустим, что UserA и UserB открыли приложение и работают с данными одного типа. UserA вставляет запись в таблицу со значением 10 (PrimaryKey = 'A'), UserB в настоящее время не видит введенное значение UserA и пытается вставить новое значение 20 (PrimaryKey = 'A'). В этой ситуации я хотел получить исключение DBConcurrencyException, но вместо этого у меня возникло нарушение первичного ключа. Я понимаю, почему, но я не знаю, как решить эту проблему. Что такое хорошая практика, чтобы справиться с таким обстоятельством? Я не хочу выполнять слияние перед обновлением базы данных, поскольку хочу сообщить об ошибке пользователю, что несколько пользователей обновили эти данные.

Ответы [ 4 ]

5 голосов
/ 12 июня 2010

В этой ситуации я хотел получить исключение DBConcurrencyException, но вместо этого у меня возникло нарушение первичного ключа.Я понимаю, почему

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

2 голосов
/ 12 июня 2010

Если вы получаете нарушения PK, когда параллельные пользователи вставляют NEW записи, то происходит одно из двух:

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

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

2 голосов
/ 12 июня 2010

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

Мне лень - цитата из этой темы :

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

  1. Ничего не делать

    • Пользователь 1 читает запись
    • Пользователь 2 читает ту же запись
    • Пользователь 1 обновляет эту запись
    • Пользователь 2 обновляет ту же запись

    Пользователь 2 переписал изменения, внесенные пользователем 1. Они полностью ушли, как будто их никогда не было. Это называется «потерянным обновлением».

  2. Блокировка записи, когда она читается. Пессимистическая блокировка

    • Пользователь 1 читает запись и блокирует ее , устанавливая монопольную блокировку записи (предложение FOR UPDATE)
    • Пользователь 2 пытается прочитать и заблокировать ту же запись, но теперь должен ждать позади Пользователя 1
    • Пользователь 1 обновляет запись (и, конечно, фиксирует)
    • Пользователь 2 теперь может читать запись с изменениями, внесенными Пользователем 1
    • Пользователь 2 обновляет запись вместе с изменениями от Пользователя 1

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

  3. Использовать оптимистическую блокировку. Оптимистическая блокировка не использует эксклюзивные блокировки при чтении. Вместо этого во время обновления выполняется проверка, чтобы убедиться, что запись не была изменена с момента ее чтения. Это можно сделать, проверив каждое поле в таблице. то есть. ОБНОВЛЕНИЕ Table1 SET Col2 = x ГДЕ COL1 =: OldCol1 И COl2 =: OldCol AND Col3 =: OldCol3 AND ... Есть, конечно, несколько недостатков. Во-первых, вы должны уже выбрать каждый столбец таблицы. Во-вторых, вы должны построить и выполнить это массивное утверждение. Большинство людей реализуют это, вместо этого, через один столбец, обычно называемый меткой времени. Этот столбец используется ни для каких других целей , кроме реализации оптимистичного параллелизма. Это может быть число или дата. Идея состоит в том, что ему присваивается значение при вставке строки. Всякий раз, когда запись читается, столбец отметки времени также читается. Когда выполняется обновление, проверяется столбец отметки времени. Если во время ОБНОВЛЕНИЯ оно имеет то же значение, что и при чтении, то все в порядке, ОБНОВЛЕНИЕ выполняется и метка времени изменяется! . Если значение метки времени отличается во время ОБНОВЛЕНИЯ, то пользователю возвращается ошибка - он должен заново прочитать запись, повторно внести свои изменения и попытаться обновить запись снова.

    • Пользователь 1 читает запись, включая метку времени 21
    • Пользователь 2 читает запись, включая метку времени 21
    • Пользователь 1 пытается обновить запись. Отметка времени в had (21) совпадает с отметкой времени в базе данных (21), поэтому обновление выполняется, а отметка времени обновляется (22).
    • Пользователь 2 пытается обновить запись. Отметка времени в руке (21) не не совпадает с отметкой времени в базе данных (22), поэтому возвращается ошибка. Пользователь 2 должен теперь перечитать запись, включая новую отметку времени (22) и изменения пользователя 1, повторно применить их изменения и повторить попытку обновления.
0 голосов
/ 12 июня 2010

Одно решение может включать INSTEAD OF INSERT Триггер на столе.

Здесь вы будете переопределять оператор INSERT в своем триггере. У вас будет шанс RAISEERROR , когда вы обнаружите, что строка или значение уже существует для первичного ключа 'A'.

...