Дедлок, вызванный оператором SELECT JOIN с SQL Server - PullRequest
7 голосов
/ 15 сентября 2010

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

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

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

Когда данные, считанные оператором, должны быть обновлены позже в транзакции с инструкциями UPDATE не гарантируется, что порядок 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. Загрузка обеих таблиц индивидуально не даст оптимизатору запросов шанс найти лучшее план выполнения. Но поскольку операторы UPDATE могут обновлять только одну таблицу за время, которое это может вызвать взаимоблокировки, когда объект загружается в то время как объект обновляется другой транзакцией. Обновления объектов часто вызывают ОБНОВЛЕНИЯ на обе таблицы, когда свойства объекта, которые принадлежат к разным типам иерархия типов обновлена.

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

Единственный обходной путь, так что до сих пор кажется, что чтение не может получить блокировки совсем. В SQL Server 2005 это можно сделать с помощью SNAPSHOT ISOLATION. единственным способом для SQL Server 2000 было бы использование изоляции READ UNCOMMITED уровень.

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

Ответы [ 3 ]

5 голосов
/ 15 сентября 2010

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

Редактировать:

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

0 голосов
/ 24 июня 2011

Еще один способ исправить это - разделить select ... from ... join на несколько операторов select. Установите уровень изоляции для чтения зафиксирован. Используйте табличную переменную для передачи данных из выбора, который будет соединен с другим. Используйте команду Different для фильтрации вставок в эти переменные таблицы.

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

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

PS Я столкнулся с этой проблемой, и это сработало очень хорошо. Намного лучше, чем мой силовой ответ.

0 голосов
/ 21 апреля 2011

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

Итак (это от пользователя "Bill the Lizard"), если у вас есть запрос FROM table1 LEFT JOIN table2 и ваше предложение WHERE содержит только столбцы из table2, план выполнения обычно сначала выбирает строки из table2, а затем ищет строки из таблицы1. При небольшом наборе результатов из таблицы2 нужно выбрать только несколько строк из таблицы1. При использовании FORCE ORDER сначала должны быть извлечены все строки из таблицы table1, поскольку в нем нет предложения WHERE, затем строки из таблицы table2 объединяются, и результат фильтруется с помощью предложения WHERE. Таким образом, ухудшается производительность.

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

Синтаксис

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