SQLServer тупик - PullRequest
       30

SQLServer тупик

5 голосов
/ 08 мая 2009

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

В попытке исправить это, я решил сделать все чтения в рассматриваемой таблице «для обновления», используя подсказку UPDLOCK, чтобы избежать тупика. Тем не менее, я все еще вижу проблему. Я включил трассировку в SQLServer и обнаружил следующую тупиковую трассировку в журналах SQLServer:

Обнаружена тупик .... Печать информации о взаимоблокировке График ожидания

Узел: 1 КЛЮЧ: 5: 72057594042384384 (54048e7b3828) CleanCnt: 3 Режим: X Флаги: 0x0 Список грантов 1: Владелец: 0x03D08C40 Режим: X Flg: 0x0 Ссылка: 0 Жизнь: 02000000 SPID: 62 ECID: 0 XactLockInfo: 0x04834274 SPID: 62 ECID: 0 Тип выписки: DELETE Строка №: 1 Input Buf: Language Event: (@ P0 nvarchar (4000)) удалить из part_data, где part_id = @ P0 Запрошенный: ResType: LockOwner Стиль: 'OR'Xdes: 0x04B511C8 Режим: U SPID: 60 BatchID: 0 ECID: 0 TaskProxy: (0x058BE378) Значение: 0x3d08500 Стоимость: (0/1296)

Node: 2

КЛЮЧ: 5: 72057594042384384 (f903d6d6e0ac) CleanCnt: 2 Режим: X Флаги: 0x0 Список грантов 0: Владелец: 0x03D088A0 Режим: X Flg: 0x0 Ссылка: 0 Жизнь: 02000000 SPID: 60 ECID: 0 XactLockInfo: 0x04B511EC SPID: 60 ECID: 0 Тип выписки: DELETE Строка №: 1 Input Buf: Language Event: (@ P0 nvarchar (4000)) удалить из part_data, где part_id = @ P0 Запрошенный: ResType: LockOwner Стиль: 'OR'Xdes: 0x04834250 Режим: U SPID: 62 BatchID: 0 ECID: 0 TaskProxy: (0x047BA378) Значение: 0x3d089e0 Стоимость: (0/4588)

Владелец ресурса жертвы: ResType: LockOwner Стиль: 'OR'Xdes: 0x04B511C8 Режим: U SPID: 60 BatchID: 0 ECID: 0 TaskProxy: (0x058BE378) Значение: 0x3d08500 Стоимость: (0/1296)

Профилировщик SQLServer показывает это как два клиента, удерживающие блокировки обновления (U) и пытающиеся перерасти в монопольные блокировки (X). Документы по SQLServer, которые я прочитал, говорят, что только один клиент может иметь (U) блокировку таблицы в данный момент времени, поэтому мне интересно, почему я вижу ситуацию, показанную в трассировке.

Объект базы данных, на который ссылается эта трассировка, является индексом внешнего ключа. Если кто-то, имеющий опыт решения этой проблемы, может дать совет, это будет большая помощь.

Спасибо, Бред.

РЕДАКТИРОВАТЬ добавленный график взаимоблокировки xml по запросу:

<deadlock-list>
 <deadlock victim="process989018">
  <process-list>
   <process id="process6aa7a8" taskpriority="0" logused="4844" waitresource="KEY: 5:72057594042384384 (5504bdfb7529)" waittime="9859" ownerId="613553" transactionname="implicit_transaction" lasttranstarted="2009-05-08T11:52:39.137" XDES="0x5fcbc30" lockMode="U" schedulerid="1" kpid="3516" status="suspended" spid="59" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-05-08T11:52:39.183" lastbatchcompleted="2009-05-08T11:52:39.183" clientapp="jTDS" hostname="LOIRE" hostpid="123" loginname="sa" isolationlevel="read committed (2)" xactid="613553" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x0200000007c76c39efdd8317c6fa7b611b4fd958f05cfcf4">
delete from part_data where part_id =  @P0     </frame>
    </executionStack>
    <inputbuf>(@P0 nvarchar(4000))delete from part_data where part_id = @P0</inputbuf>
   </process>
   <process id="process989018" taskpriority="0" logused="1528" waitresource="KEY: 5:72057594042384384 (5e0405cb0377)" waittime="1250" ownerId="613558" transactionname="implicit_transaction" lasttranstarted="2009-05-08T11:52:39.183" XDES="0x48318f0" lockMode="U" schedulerid="2" kpid="2692" status="suspended" spid="60" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-05-08T11:52:39.183" lastbatchcompleted="2009-05-08T11:52:39.183" clientapp="jTDS" hostname="LOIRE" hostpid="123" loginname="sa" isolationlevel="read committed (2)" xactid="613558" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x0200000007c76c39efdd8317c6fa7b611b4fd958f05cfcf4">
delete from part_data where part_id =  @P0     </frame>
    </executionStack>
    <inputbuf>(@P0 nvarchar(4000))delete from part_data where part_id =  @P0</inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057594042384384" dbid="5" objectname="MESSAGESTOREDB61.dbo.part_data" indexname="idx_part_data_part_id" id="lock3cab740" mode="X" associatedObjectId="72057594042384384">
    <owner-list>
     <owner id="process6aa7a8" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process989018" mode="U" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057594042384384" dbid="5" objectname="MESSAGESTOREDB61.dbo.part_data" indexname="idx_part_data_part_id" id="lock3cad340" mode="X" associatedObjectId="72057594042384384">
    <owner-list>
     <owner id="process989018" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process6aa7a8" mode="U" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

Ответы [ 7 ]

4 голосов
/ 11 июня 2009

Добро пожаловать в ужас.

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

Так что, если вы можете изолировать некоторые запросы, проверьте sql для них и посмотрите, не можете ли вы поэкспериментировать с предоставлением некоторых покрывающих индексов.

Покрывающий индекс - это индекс, который включает все поля в определенном предложении where.

2 голосов
/ 08 мая 2009

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

Пример (псевдо шаги)

начало транс

  • запись данных (например, INSERT, UPDATE) в таблицу Foo
  • считывает некоторые данные (которые запускают сканирование таблицы) из Foo, используя другое соединение DEADLOCK, поскольку чтение никогда не заканчивается, поэтому транзакция никогда не совершается
1 голос
/ 06 ноября 2009

Сначала не используйте подсказки, обычно SQL Server лучше оставить самостоятельно.

Во-вторых, убедитесь, что у вас нет НАСТОЯЩЕЙ тупиковой блокировки (но это случается гораздо реже, поскольку появление тупиковой ситуации может намекнуть).

В-третьих, как кто-то предложил, проверьте, нет ли у вас медленного запроса и настройте его.

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

Я не вижу, о какой версии SQL Server вы говорите, но каждая следующая версия имеет лучшее управление взаимоблокировками, чем предыдущая, и SQL Server 2000 был особенно неприятен в этом разделе.

Привет
Massimo

0 голосов
/ 30 ноября 2016

Прежде всего, это два оператора удаления. Эти два SPID выполняют один и тот же оператор

delete from part_data where part_id = @P0

и, вероятно, они не будут пытаться удалить одну и ту же запись. Здесь происходит взаимоблокировка, потому что SQLServer не может идентифицировать строку / строки из таблицы part_data, используя столбец part_id, а также блокирует другие записи.

Итак, у вас есть два варианта

  1. Сделать part_id своим первичным ключом (если возможно)

    или

  2. Измените утверждение следующим образом. (Предполагая, что есть первичный ключ)

    Select primary_key into #temp From part_data where part_id = @p0

    - вы получите первичный ключ тех записей, которые должны быть удалены

    delete a from part_data a join #temp b on a.primary_key = b.primary_key

- Вы можете удалить те записи, которые должны быть удалены, не блокируя другие записи

0 голосов
/ 27 декабря 2013

Много лет назад я сталкивался с той же проблемой за столом, на который нацелено более одного миллиарда транзакций в день.

У нас было четыре службы tomcat для обработки запросов одного веб-сайта. Иногда две разные службы пытались обработать один и тот же запрос, который появляется, когда трафик веб-сайта был на самом низком уровне. Операция удаления первого запроса блокировала IX, а второй запрос, выполненный в то же время, делал то же самое.

Мы решили эту проблему, создав буферную таблицу. Действия удаления были сохранены в этой таблице. Каждую секунду задание sql считывает эту таблицу, чтобы удалить строки, помеченные как «подлежащие удалению».

0 голосов
/ 20 июля 2009

... Я решил сделать все чтения на рассматриваемая таблица должна быть сделана "для обновления" используя подсказку UPDLOCK, чтобы можно избежать тупика ...

... Да, транзакция объявлена убедитесь, что весь граф объекта удалены или оставлены нетронутыми, если ошибка. Там нет другой базы данных операции в транзакции, некоторые чтения выполняются до транзакции запущен, но внутри его нет ...

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

Как говорится ...

Звучит так, будто вы пытаетесь удалить несколько строк в каждой транзакции и что происходит взаимоблокировка, потому что SQL повышает уровень блокировки таблицы для каждой. Помните, что в блокировках строк SQL Server Key-Range и Page все сразу перерастает в Table Locks. Если у вас есть несколько транзакций, удаляющих несколько строк, они попытаются обостриться, и вы получите тупики.

Это снизит производительность, если у вас много пользователей, но попробуйте указать подсказки TABLOCK в ваших операторах удаления и посмотреть, исчезнет ли проблема.

Также взгляните на планы выполнения ваших операторов SQL и посмотрите, как происходит повышение блокировки.

0 голосов
/ 08 мая 2009

Я предполагаю, что вы запустили что-то вроде: DBCC TRACEON (1222, -1) "и / или" DBCC TRACEON (1204, -1). Я нахожу трассировку тупиковой ситуации в журналах SQLServer трудной для чтения. Вы уверены, что это попытка перейти на эксклюзивные (X) блокировки?

Попробуйте запустить трассировку в профилировщике (выберите пустой шаблон), выберите «событие графика взаимоблокировки» и на новой вкладке, которая появляется (Настройки извлечения событий), сохраните каждое (установите флажок «сохранять события XML взаимоблокировки отдельно»). ") в своем собственном файле. Откройте этот файл в программе просмотра XML, и вам будет легко узнать, что происходит. Каждый процесс содержится со стеком выполнения вызовов процедур / триггеров и т. Д. И т. Д., И все блокировки также присутствуют там. Мне трудно поверить, что два DELETE, где col = @ value вызывают проблему, посмотрите на стек выполнения, есть ли триггер или что-то еще происходит?

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

...