Решение ситуации тупика SQL Server - PullRequest
2 голосов
/ 15 марта 2010

Я пытаюсь найти решение, которое разрешит повторяющуюся тупиковую ситуацию на SQL-сервере. Я провел некоторый анализ графика взаимоблокировки, генерируемого трассировкой профилировщика, и получил следующую информацию:

Первый процесс (spid 58) выполняет этот запрос:

UPDATE cds.dbo.task_core
    SET nstate = 1 
    WHERE nmboxid = 89 AND ndrawerid = 1 
        AND nobjectid IN (SELECT
                              nobjectid 
                              FROM (SELECT
                                        nobjectid, count(nobjectid) AS counting
                                        FROM cds.dbo.task_core
                                        GROUP BY nobjectid
                                   ) task_groups
                              WHERE task_groups.counting > 1
                         )          

Второй процесс (spid 86) выполняет этот запрос:

INSERT INTO task_core (…) VALUES (…)

spid 58 ожидает блокировки Shared Page на CDS.dbo.task_core (spid 86 содержит конфликтующее намеренное исключение (IX) блокировка)

spid 86 ожидает блокировки Intent Exclusive (IX) на CDS.dbo.task_core (spid 58 содержит конфликтующее обновление блокировку)

Ответы [ 4 ]

2 голосов
/ 15 марта 2010

Хорошо, что вы опубликовали заявления и ресурсы. Чтобы полностью понять проблему, планы также будут полезны. Но я собираюсь сделать (образованное) предположение и диагностировать причину тупика как большое сканирование, происходящее в подзапросе ОБНОВЛЕНИЯ:

SELECT nobjectid 
    FROM (SELECT nobjectid, count(nobjectid) AS counting
          FROM cds.dbo.task_core
           GROUP BY nobjectid
    ) task_groups
    WHERE task_groups.counting > 1

Этот запрос должен сканировать всю таблицу task_core. Всегда. Вы зашли в тупик на странице, потому что полное сканирование таблицы оптимизировано для использования блокировки страницы, но вы также можете попасть на уровень строки, если добавите подсказку ROWLOCK.

Чтобы устранить взаимоблокировку, необходимо устранить конфликт во время полного сканирования таблицы, который происходит в обновлении. Вместо использования «грязного» чтения можно попробовать использовать управление версиями на уровне строк, включить изоляцию моментальных снимков при чтении в базе данных, см. Общие сведения об уровнях изоляции на основе управления версиями строк .

Но намного лучшим решением было бы не сканировать в первую очередь. Сначала вернитесь к требованиям бизнес-логики и вашей модели данных. Каждый раз, когда вы видите обновление, которое требует просмотра всей таблицы , чтобы принять решение, это очень вонючий запах кода. Если вы действительно обнаружите, что обновление не может быть переписано более разумным способом (я сомневаюсь), то вам следует рассмотреть возможность использования индексированного представления. Выражения BIG_COUNT (*) разрешены в индексированных представлениях, и они значительно ускорят запрос , в дополнение к устранению причины тупика.

1 голос
/ 16 марта 2010

Я подозреваю, что, как и другие здесь, производительность частей вашего запроса может быть улучшена, чтобы уменьшить вероятность взаимоблокировок. Однако я также подозреваю, что могут быть законные и неизбежные случаи, когда действительно существует «потребность» в тупике. Это очень сильно зависит от вашего оператора вставки и от того, сколько строк вы добавляете за раз, если используется многокомпонентный ключ (например, записи добавляются только в конце таблицы из-за идентификационного ключа или они вставлен через стол из-за составного ключа).

Лучший пример того, почему я думаю, что у вас может быть настоящая проблема, это: что, если вы вставляете запись, которая имеет nmboxid = 89 AND ndrawerid = 1 и отражает количество объектов> 1?

Я не хочу отговаривать вас от расследования правильного решения первопричины; но с другой стороны, мне интересно, является ли самое простое решение (по крайней мере, в качестве первого шага) правильной обработкой, когда возникает тупик.

1 голос
/ 15 марта 2010

С манжеты, я думаю, что ваш самый внутренний подзапрос в spid 58 ожидает INSERT (spid 86).

Если в подзапросе разрешено грязное чтение, попробуйте добавить "WITH (NOLOCK)".

(
    SELECT nobjectid, count(nobjectid) AS counting
    FROM cds.dbo.task_core WITH (NOLOCK)
    GROUP BY nobjectid
)
0 голосов
/ 15 марта 2010

Вам не нужно иметь производную таблицу в подзапросе, ваш исходный запрос может быть:

UPDATE cds.dbo.task_core
    SET nstate = 1 
    WHERE nmboxid = 89 AND ndrawerid = 1 
        AND nobjectid IN (SELECT
                              nobjectid
                              FROM cds.dbo.task_core
                              GROUP BY nobjectid
                              HAVING COUNT(nobjectid)>1
                         )

но это не помешает тупику. Можете ли вы добавить ГДЕ в подзапрос? как:

UPDATE cds.dbo.task_core
    SET nstate = 1 
    WHERE nmboxid = 89 AND ndrawerid = 1 
        AND nobjectid IN (SELECT
                              nobjectid
                              FROM cds.dbo.task_core
                              WHERE nmboxid = 89 AND ndrawerid = 1   --<<<<<<<
                              GROUP BY nobjectid
                              HAVING COUNT(nobjectid)>1
                         )

это может (если можно использовать индекс) предотвратить сканирование таблицы и разрешить процесс INSERT.

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