Когда я обновляю / вставляю одну строку, должна ли она блокировать всю таблицу? - PullRequest
26 голосов
/ 12 февраля 2010

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

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

Два вопроса:

  1. Когда я обновляю / вставляю одну строку, блокируется ли вся таблица?
  2. Что можно сделать, чтобы обойти эту проблему?

Ответы [ 4 ]

21 голосов
/ 12 февраля 2010

Обычно нет, но это зависит (наиболее часто используемый ответ для SQL Server!)

SQL Server должен каким-то образом блокировать данные, участвующие в транзакции. Он должен заблокировать данные в самой таблице, а также данные любых затронутых индексов, пока вы выполняете модификацию. Чтобы улучшить параллелизм, существует несколько «гранулярностей» блокировок, которые сервер может решить использовать, чтобы разрешить запуск нескольких процессов: блокировки строк, блокировки страниц и таблицы часто встречаются (их больше). Какой масштаб блокировки используется, зависит от того, как сервер решит выполнить данное обновление. Сложные вещи, существуют также классификации блокировок, таких как разделяемые, исключительные и исключающие намерения, которые определяют, можно ли читать и / или изменять заблокированный объект.

По моему опыту, SQL Server в основном использует блокировки страниц для изменений небольших частей таблиц, и после превышения некоторого порогового значения он автоматически переходит в блокировку таблицы, если на большую часть таблицы (по статистике) влияет обновление или удаление. Идея состоит в том, что быстрее блокировать таблицу (одну блокировку), чем получать и управлять тысячами отдельных блокировок строк или страниц для большого обновления.

Чтобы увидеть, что происходит в вашем конкретном случае, вам нужно взглянуть на логику запроса и, пока ваши материалы работают, изучить условия блокировки / блокировки в sys.dm_tran_locks, sys.dm_os_waiting_tasks или других DMV. Вы бы хотели узнать, что именно блокируется на каком шаге в каждом из ваших процессов, чтобы понять, почему один из них блокирует другой.

19 голосов
/ 12 февраля 2010

Краткая версия:

  1. Нет
  2. Исправьте ваш код.

Длинная версия:

LCK_M_IX - намеренная блокировка, означающая, что операция установит X-блокировку для подчиненного элемента. Например. При обновлении строки в таблице операционная таблица устанавливает блокировку IX на таблицу перед блокировкой X строки, которая обновляется / вставляется / удаляется. Преднамеренные блокировки - это общая стратегия для работы с иерархиями, такими как таблица / страница / строка, поскольку менеджер блокировок не может понять физическую структуру ресурсов, запрошенных для блокировки (т. Е. Он не может знать, что X-блокировка на странице P1 несовместима с S-блокировка в строке R1, потому что R1 содержится в P1). Подробнее см. Режимы блокировки .

Тот факт, что вы видите состязание при намеренных блокировках, означает, что вы пытаетесь получить объектные блокировки высокого уровня, такие как блокировки таблиц. Вам нужно будет проанализировать исходный код на предмет блокировки запроса (тот, который запрашивает блокировку, несовместимую с LCK_M_IX) и устранить причину запроса блокировки уровня объекта. Что это значит, будет зависеть от вашего исходного кода, я не могу знать, что вы там делаете. Я предполагаю, что вы используете ошибочную подсказку блокировки.

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

11 голосов
/ 12 февраля 2010

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

Узнайте, что делает каждый запрос, и если вы получаете повышение блокировок, чего вы не ожидаете. То, что вы говорите WITH (ROWLOCK) в запросе, не означает, что SQL Server сможет соответствовать ... если вы затронули несколько индексов, индексированных представлений, постоянных вычисляемых столбцов и т. Д., То есть множество причин, по которым ваша блокировка строки не может держать воду. У вас также могут быть вещи позже в транзакции, которые занимают больше времени, чем вы думаете, и, возможно, вы не понимаете, что блокировки для всех объектов, участвующих в транзакции (не только для оператора, который выполняется в данный момент), могут быть сохранены для длительность сделки.

1 голос
/ 12 февраля 2010

Различные базы данных имеют разные механизмы блокировки, но такие, как SQL Server и Oracle, имеют разные типы блокировки.

По умолчанию в SQL Server используется пессимистическая блокировка страниц, поэтому, если у вас небольшое количество записей, все они могут быть заблокированы.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...