Как избежать тупика между операторами вставки / удаления из-за не кластерных неуникальных индексов! - PullRequest
10 голосов
/ 15 марта 2011

Недавно я натолкнулся на сценарий взаимоблокировки на OLTP-боксе (Sql server 2005) клиента и обнаружил, что это вызвано двумя хранимыми процедурами, вызываемыми двумя различными потоками.данные в таблице X.

Insert Into X (col1 , col2  , col3  ) 
Values ('value 1' , 'value 2'  , 'value 3'  )

2, Удалить sp, который удаляет данные из таблицы X.

  DELETE X  
  FROM X T1 WITH (NOLOCK)   
  INNER JOIN Y T2 WITH (NOLOCK)
  ON T1.[col2] = T2.[col2]  
   WHERE t2.date < 'date time value'  

Таблица X имеет один уникальный кластеризованный первичный ключ и два некластеризованных неуникальныхиндексов.Я проанализировал тупик, установив флаг tace t1222, и результат суммирован ниже;

Вставка sp получила блокировку IX для некластеризованного индекса для столбца 1. Удаление sp ожидает блокировки X для того же некластеризованного индекса для столбца 1. В это время

Удаление sp приобрело aU-блокировка для некластеризованного индекса для столбца 2. Вставка sp ожидает блокировки IX для того же некластеризованного индекса для столбца 2. В течение этого времени.

Любая идея или предложение, позволяющее избежать тупиковой ситуации, будет принята с благодарностью.1015 *

РЕДАКТИРОВАТЬ

вывод флага трассировки t1222

deadlock-list  
deadlock victim=process3c77d68  
process-list  
process id=process3c12c58 taskpriority=0 logused=1044 waitresource=PAGE: 17:8:7726 waittime=1250 ownerId=5169682909 transactionname=user_transaction lasttranstarted=2011-02-03T03:34:03.443 XDES=0xfe64d78b0 lockMode=IX schedulerid=2 kpid=9544 status=suspended spid=219 sbid=0 ecid=0 priority=0 transcount=2 lastbatchstarted=2011-02-03T03:34:03.457 lastbatchcompleted=2011-02-03T03:34:03.453 clientapp=.Net SqlClient Data Provider hostname=HQMTSRV026 hostpid=3308 loginname=EASUser isolationlevel=read committed (2) xactid=5169682909 currentdb=17 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056  
executionStack  
frame procname=adhoc line=1 stmtend=296 sqlhandle=0x0200000084ce2a1d0e95a5623fa3a9c0981d422e33cab999  
(@1 int<c/>@2 varchar(8000)<c/>@3 nvarchar(4000))INSERT INTO [VB_Audit_TransactionDetail]([ItemID]<c/>[TransactionID]<c/>[ItemValue]) values(@1<c/>@2<c/>@3)
frame procname=adhoc line=1 stmtend=296 sqlhandle=0x02000000afcb1733f435fb93e13556600acf32bb32e10020
Insert Into VB_Audit_TransactionDetail (ItemID  <c/> TransactionID  <c/> ItemValue  ) Values (4 <c/> '0255978c-f56e-477e-b361-8abe62433cff'  <c/> N'HQOLB006'  )  
frame procname=EAS.dbo.SP_Insert line=13 stmtstart=482 stmtend=522 sqlhandle=0x03001100805efa5997d69400719600000100000000000000  
exec (@CommandText)  
inputbuf  
Proc [Database Id = 17 Object Id = 1509580416]  
process id=process3c77d68 taskpriority=0 logused=364 waitresource=PAGE: 17:6:334008 waittime=1234 ownerId=5169682116 transactionname=user_transaction lasttranstarted=2011-02-03T03:34:03.053 XDES=0xa8e297cd0 lockMode=X schedulerid=12 kpid=10300 status=suspended spid=327 sbid=0 ecid=0 priority=0 transcount=2 lastbatchstarted=2011-02-03T03:33:41.137 lastbatchcompleted=2011-02-03T03:33:41.133 clientapp=Microsoft SQL Server hostname=HQSSISSRV002 hostpid=7632 loginname=NBKDOM\SQLCSRVC isolationlevel=read committed (2) xactid=5169682116 currentdb=17 lockTimeout=4294967295 clientoption1=671350816 clientoption2=128056  
executionStack  
frame procname=EAS.dbo.PurgeAuditTransactionTables line=59 stmtstart=4202 stmtend=4728 sqlhandle=0x030011006354a2313d11ae00979a00000100000000000000  
DELETE [dbo].[Audit_TransactionDetail]  
FROM [dbo].[Audit_TransactionDetail] T1 WITH (NOLOCK)  
INNER JOIN [dbo].[Audit_NBKTransaction] T2 WITH (NOLOCK)ON T1.[TransactionID] = T2.[TransactionID]  
WHERE TransactionPostedDateTime < @LastReplicationDateTime  
frame procname=adhoc line=1 sqlhandle=0x0100110096968c0560c430ff190000000000000000000000  
EXEC PurgeAuditTransactionTables '02 Feb 2011 19:00:13:870'  
inputbuf  
EXEC PurgeAuditTransactionTables '02 Feb 2011 19:00:13:870'  
resource-list  
pagelock fileid=8 pageid=7726 dbid=17 objectname=EAS.dbo.Audit_TransactionDetail id=lock4f79500 mode=U associatedObjectId=886415243542528  
owner-list  
owner id=process3c77d68 mode=U  
waiter-list  
waiter id=process3c12c58 mode=IX requestType=wait  
pagelock fileid=6 pageid=334008 dbid=17 objectname=EAS.dbo.Audit_TransactionDetail id=lock846afca00 mode=IX associatedObjectId=604940266831872  
owner-list  
owner id=process3c12c58 mode=IX  
waiter-list  
waiter id=process3c77d68 mode=X requestType=wait  

Еще одна важная вещь;операторы удаления и вставки всегда затрагивают 2 разных набора данных.

Ответы [ 2 ]

7 голосов
/ 15 марта 2011

Вместо того, чтобы публиковать описание вашего понимания графа взаимоблокировки, опубликуйте сам график взаимоблокировки. XML, а не растровое изображение графического рендеринга. На первый взгляд, тот факт, что существует конфликт между задействованной блокировкой IX, указывает на то, что имеет место эскалация блокировки, что указывает на отсутствие индекса для обслуживания DELETE или что достигнута точка опрокидывания индекса в соединении. Но опять же, это просто предположение из-за недостатка информации. Чтобы дать какой-либо осмысленный ответ, понадобится фактический XML-код тупика и точное определение схемы задействованного объекта.

после обновления

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

Ваша оценка, что «операторы удаления и вставки всегда затрагивают 2 разных набора данных» неверна для двух учетных записей:

  • когда запрос выполняет сканирование таблицы, это автоматически подразумевает, что он будет касаться всех данных, независимо от того, какие строки действительно соответствуют
  • даже в правильно настроенных базах данных, где все операции имеют закрывающие индексы, блокировки хешируются, а хэши конфликтуют гораздо чаще, чем можно было бы ожидать. Большое сканирование будет конфликтовать с большим количеством только его собственных отсканированных строк, из-за парадокса дня рождения . См. %% lockres %% Магия вероятности столкновения: 16,777,215 .

В качестве примечания, таблицы аудита почти всегда должны быть сгруппированы по дате / времени инцидента, потому что все запросы к ним запрашивают определенные интервалы времени («что произошло между ... и ...»), и поиск элементов может быть удовлетворяется некластеризованным первичным ключом в ID. Очистка записей аудита, даже при правильной кластеризации, связана с проблемами производительности и требует пакетной обработки, чтобы избежать взрыва журнала. Наилучшим решением является развертывание автоматизированного скользящего окна с использованием разбиения , но это сопряжено со своими проблемами.

6 голосов
/ 15 марта 2011

Я думаю, таблица T2 должна быть довольно большой. Индексируется ли столбец t2.date? Если нет, то сканирование таблицы на большом столе может вызвать ваши проблемы. Индексирование этого столбца может оптимизировать удаление, избегая сканирования таблицы. В качестве альтернативы, если индексы на col1 или col2 действительно не используются (или используются недостаточно), их удаление также может избежать этой проблемы.

Как часто возникают эти тупики? Если они встречаются редко, может быть достаточно хитрого обходного пути: оберните каждый оператор в блок try / catch, при проверке перехвата, если ошибка произошла из-за тупика, и, если это так, повторите команду. Вы также можете осторожно использовать SET DEADLOCK_PRIORITY, чтобы выбрать, какой запрос будет всегда выигрывать / проигрывать (но вы должны сбалансировать это со всеми вызовами таблицы).

О, и брось эти WITH (NOLOCK) с. NOLOCK игнорируется вставками, обновлениями и удалениями.

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