Распространенный способ сделать проверку на отсутствие строки и вставить ее атомарной? - PullRequest
3 голосов
/ 27 января 2010

У меня есть веб-приложение. Процесс обработки формы в нем выглядит так:

  1. Validate
  2. Список ошибок или вставка / обновление данных

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

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

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

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

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

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


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

Стол блокировочный

Я уже решил эту проблему в прошлом с помощью блокировки таблицы, но я не уверен, что это был лучший способ сделать это. Процесс пошел так:

  1. Блокировка таблицы для записи
  2. Проверить наличие мест
  3. Вернуть ошибку или вставить строку
  4. Разблокировать стол

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

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

Вставить и поймать ошибку

Так мне предложили другие ребята. Они предложили сделать этот столбец уникальным индексным столбцом и раздельно проверять и проверять уникальность в два этапа. Процесс идет так:

  1. Проверка данных
  2. Если проверка прошла успешно, вставьте строку
  3. Если при вставке строки произошла ошибка, отобразится ошибка недоступности уникального значения

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

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

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

Ответы [ 3 ]

1 голос
/ 27 января 2010

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

Try / catch - это способ выполнения вставок с привязкой к уникальному ключу. На мой взгляд, это лучший метод. Вы должны понять, что любая транзакционная база данных, использующая блокировку на уровне строк, уязвима для тупиков в любое время . Даже по обычным, не смешным вещам. Учитывая это, любое приложение, использующее транзакционную базу данных, должно технически иметь каждый выполненный запрос записи внутри блока try / catch!

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

Суть в том, что чем больше блокировок вы можете избежать, тем более масштабируемым будет ваше приложение. Даже если вы можете использовать SELECT...FOR UPDATE для несуществующего значения, это может значительно увеличить количество взаимоблокировок в этой таблице. Так как этого легко избежать, используя try / catch, я всегда использовал try / catch. Кроме того, довольно просто собрать универсальную оболочку обработчика ошибок для драйвера базы данных, чтобы выбрать типичные ошибки, такие как уникальный ключ или тупик, и обработать их соответствующим образом.

0 голосов
/ 27 января 2010

Создать новую таблицу AvailableUsers , столбцы которой ID, ClaimedUserName, TimeStamp, SessionID или сохраните в сеанс в виде объекта.

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

0 голосов
/ 27 января 2010

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

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

...