Страницы

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

суббота, 20 апреля 2019 г.

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

Допустим, есть процедура, которая содержит в себе запрос вида:
UPDATE TOP (1) table SET SessionId=@SessionId Where SessionId is null
Если эту процедуру будут запускать параллельно несколько пользователей, то не будет ли коллизий в том, что одном и тот же юзер проапдейтит одну и туже запись или MS SQL сервер позаботится, что бы такой ситуации не возникло?
Если такое имеет место быть, то как этого избежать?


Ответ

Такая конструкция потокобезопасна.
или 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-блокировку, если запись выбрана, с последующим освобождением, - скорее всего будет сохраняться.

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

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