Страницы

Поиск по вопросам

понедельник, 10 февраля 2020 г.

Параллельное обновление таблицы

#sql #sql_server


Допустим, есть процедура, которая содержит в себе запрос вида:

UPDATE TOP (1) table
SET SessionId=@SessionId
Where SessionId is null


Если эту процедуру будут запускать параллельно несколько пользователей, то не будет
ли коллизий в том, что одном и тот же юзер проапдейтит одну и туже запись или MS SQL
сервер позаботится, что бы такой ситуации не возникло?

Если такое имеет место быть, то как этого избежать?
    


Ответы

Ответ 1



Такая конструкция потокобезопасна. или MS SQL сервер позаботится, чтобы такой ситуации не возникло? Если кратко, то - да. Если одна сессия (имеется ввиду SQL-сессия) выбрала запись в таблице для обновления, то параллельная сессия не сможет (из-за установленной первой сессией U-блокировки) выбрать и обновить ту же самую запись (если я правильно понял - это то, что интересует). Пример с пояснениями - ниже. Пусть есть таблица: CREATE TABLE LockTest ( Id int identity not NULL primary key, SessionId uniqueidentifier NULL ); Добавим в неё немного данных: INSERT INTO LockTest (SessionId) VALUES (NULL), (NULL), (NULL), (NULL), (NULL); Пару раз выполним запрос: DECLARE @sessionId uniqueidentifier = newid(); UPDATE TOP (1) LockTest SET SessionId = @sessionId WHERE SessionId IS NULL; Выполним запрос на обновление в третий раз, запустив, предварительно профилировщик, и посмотрим какие происходят блокировки: (для полноты понимания происходящего желательно, конечно, иметь представление о режимах, гранулярности и совместимости блокировок). Происходит примерно следующее. SqlServer сканирует кластерный индекс в поисках записи, у которой SessionId является NULL. При этом для каждой записи поочередно: ставится U-блокировка на запись (чтение с возможным изменением данных) происходит проверка условия SessionId IS NULL если запись не удовлетворяет условию, то блокировка снимается и происходит переход к следующей записи (на снимке экрана - это Acquired-Released пары U-блокировок записей со значениями хэшей ключа (8194443284a0) и (61a06abd401c)) если запись удовлетворяет условию (запись со значением хэша ключа (98ec012aa510)), то блокировка повышается до монопольной (X-блокировки), запись обновляется, после чего блокировки снимаются Параллельная сессия не сможет обновить ту же самую запись, т.к. для этого ей придётся точно также установить U-блокировку на ту же самую запись. Однако две U-блокировки несовместимы, поэтому параллельной сессии придётся ждать, пока мы обновим запись и/или снимем блокировку. Тогда же, когда блокировка нами будет уже снята, SessionId у записи станет не NULL. Вследствие чего параллельной сессии придётся искать следующую "свободную" запись. Картина происходящего может несколько меняться в зависимости от наличия или отсутствия индекса на столбце SessionId. От того, будет ли проиcходить lock-escalation (для TOP (1), наверное, не должно). Однако характер происходящего, - установка U-блокировки на кандидата и её освобождение, если запись не подошла, либо преобразование в X-блокировку, если запись выбрана, с последующим освобождением, - скорее всего будет сохраняться.

Комментариев нет:

Отправить комментарий