Взаимоблокировка, вызванная инструкцией SELECT JOIN с SQL Server

при выполнении инструкции SELECT с соединением двух таблиц SQL Server, похоже, заблокируйте обе таблицы оператора по отдельности. Например, запрос это:

SELECT ...
FROM
    table1
    LEFT JOIN table2
        ON table1.id = table2.id
    WHERE ...

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

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

T1: SELECT ... FROM ... JOIN ...
T1: UPDATE table1 SET ... WHERE id = ?
T2: SELECT ... FROM ... JOIN ... (locks table2, then blocked by lock on table1)
T1: UPDATE table2 SET ... WHERE id = ?

обе таблицы представляют иерархию типов и всегда загружаются вместе. Так что имеет смысл загрузить объект с помощью SELECT с помощью JOIN. Загрузка обеих таблиц индивидуально не даст оптимизатору запросов a шанс найти лучшее план исполнения. Но поскольку операторы UPDATE могут обновлять только одну таблицу в время это может вызвать взаимоблокировки при загрузке объекта во время загрузки объекта обновляется другой транзакцией. Обновления объектов часто вызывают обновления на обе таблицы, когда свойства объекта, которые принадлежат к различным типам иерархия типов обновляется.

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

единственная работа-А-раунд, так что до сих пор кажется, что чтения не могут получить блокировки совсем. В SQL Server 2005 Это можно сделать с помощью изоляции моментальных снимков. Этот единственный способ для SQL Server 2000-использовать НЕЗАФИКСИРОВАННУЮ изоляцию чтения уровень.

Я хотел бы знать, есть ли еще одна возможность предотвратить SQL Server от создания этих тупиков?

3 ответов


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

Edit:

У меня нет доступа к SQL 2000, но я бы попытался сериализовать доступ к объекту с помощью sp_getapplock, чтобы чтение и изменения никогда не выполнялись одновременно. Если вы не можете использовать sp_getapplock, разверните свой собственный мьютекс.


другой способ исправить это, чтобы разделить выбор... от... объединение в несколько операторов select. Установить уровень изоляции чтения зафиксированных данных. Используйте табличную переменную для передачи данных из select в other. Используйте distinct для фильтрации вставок в эти переменные таблицы.

Итак, если у меня есть две таблицы A, B. я вставляю/обновляю в A, а затем B. где, поскольку оптимизатор запросов sql предпочитает сначала читать B и A. Я разделю один выбор на 2 выбора. Сначала я прочту Б. Затем передайте эти данные следующему оператору select, который читает A.

здесь взаимоблокировка не произойдет, потому что блокировки чтения в таблице B будут выпущены, как только 1-й оператор будет выполнен.

PS я столкнулся с этой проблемой, и это сработало очень хорошо. Гораздо лучше, чем мой приказ.


я столкнулся с такой же проблемой. Использование query hint FORCE ORDER исправит эту проблему. Недостатком является то, что вы не сможете использовать лучший план, который оптимизатор запросов имеет для вашего запроса, но это предотвратит взаимоблокировку.

Итак (это от пользователя "Билл ящерица") если у вас есть запрос от table1 LEFT JOIN table2 и Ваше предложение WHERE содержит только столбцы из table2, план выполнения обычно сначала выбирает строки из table2, а затем ищет строки из table1. С небольшой результирующий набор из table2 только несколько строк из table1 должны быть извлечены. С FORCE ORDER сначала все строки из table1 должны быть извлечены, потому что у него нет предложения WHERE, затем строки из table2 объединяются и результат фильтруется с помощью предложения WHERE. Таким образом, ухудшается производительность.

но если вы знаете, что это не так, используйте это. Возможно, вы захотите оптимизировать запрос вручную.

синтаксис

SELECT ...
FROM
    table1
    LEFT JOIN table2
        ON table1.id = table2.id
    WHERE ...
OPTION (FORCE ORDER)