2009-05-08 5 views
5

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

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

Тупик встречается .... Печать информации тупиковой Wait-для графа

Node: 1 KEY: 5: 72057594042384384 (54048e7b3828) CleanCnt: 3 Режим: X Флаги: 0x0 Список грантов 1: Владелец: 0x03D08C40 Режим: X Flg: 0x0 Ref: 0 Life: 02000000 SPID: 62 ECID: 0 XactLockInfo: 0x04834274 SPID: 62 ECID: 0 Statement Тип: DELETE Строка #: 1 Ввод Buf: Язык Событие: (@ P0 nvarchar (4000)) удалить из part_data, где part_id = @ P0 Запрошено: ResType: LockOwner Stype: 'OR'Xdes: 0x04B511C8 режим: U SPID: 60 BatchID: 0 ОЭСИ: 0 TaskProxy: (0x058BE378) Значение: 0x3d08500 Стоимость: (0/1296)

Узел: 2

KEY: 5: 72057594042384384 (f903d6d6e0ac) CleanCnt: 2 Режим: X Flags: 0x0 Grant Список 0: Владелец: 0x03D088A0 Режим: X Flg: 0x0 Ссылка: 0 Жизнь: 02000000 ИСП: 60 ОЭСИ: 0 XactLockInfo: 0x04B511EC ИСП: 60 ECID: 0 Тип оператора: DELETE Строка #: 1 Ввод Buf: Язык Событие: (@ P0 nvarchar (4000)) удалить из part_data, где part_id = @ P0 Запрошено: ResType: LockOwner Stype: 'OR'Xdes: 0x04834250 Режим: U SPID: 62 BatchID: 0 ECID: 0 TaskProxy :(0x047BA378) Значение: 0x3d089e0 Стоимость: (0/4588)

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

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

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

Thanks, Brad.

EDIT добавил тупиковый граф 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> 
+0

У вас есть каскад-удаление где-нибудь? –

+0

Привет, Стефан. Нет, у меня нет ограничений каскадного удаления. – Brad

+0

Формат веб-экрана не имеет значения, скопируйте/вставьте в локальный файл .XML и откройте его, и он будет выглядеть хорошо. –

ответ

0

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

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

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

+0

Привет, спасибо за комментарий. Раньше я использовал профилировщик, но теперь вижу, что неправильно интерпретировал вывод. Из списка ресурсов XML появляется два клиента, у которых уже есть (X) блокировки и на самом деле запрашивают (U) блокировки. В стеке выполнения показаны только две операции удаления. – Brad

+0

Каким образом удаляются вы пытаетесь сделать в каждом процессе? несколько или только один для каждого процесса? Если вы выполняете несколько удалений в цикле (с транзакцией) в каждом процессе, у вас могут возникнуть проблемы с блокировкой, поскольку блокировки не находятся в строках, а на страницах нескольких строк. отредактируйте свой вопрос и включите график тупика xml, чтобы я мог лучше видеть, что вы видите. –

+0

Сделка выполняет удаление на графике объектов, которые собираются вместе, чтобы составить логическую единицу данных. Таблица, в которой происходит сбой удаления, соответствует самому нижнему объекту на этом графике. Вероятно, существует более одного из этих объектов, но удаление выполняется только один раз на этом уровне, так как это «delete where foreign key =?». Мне все еще интересно, почему тупик происходит по индексу, а не по самой таблице. – Brad

2

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

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

начать транс

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

Франс, это интересный момент. Материал JDBC выполняется с помощью шаблона Spring JDBC, поэтому я сам не обрабатываю какие-либо соединения, но возможно, что чтение выполняется внутри транзакции рядом с обновлением или удалением.Я собираюсь вернуться и посмотреть, как вы описали ситуацию – Brad

+0

После некоторого дальнейшего расследования я заметил, что в одной из подозрительных транзакций есть вставка, смешанная с удалением. Мое приложение использует четыре класса обслуживания для доступа к базе данных, которые группируют операции как чтение, обновление, вставку и удаление. Я предполагаю, что проблема может заключаться в том, что вставка использует другое соединение с удалением, поскольку оно находится в отдельном классе шаблонов JDBC. Я думал, что управление транзакциями Spring заставит операции использовать одно соединение, возможно, я ошибаюсь? – Brad

+0

Я никогда не работал с Spring, поэтому я понятия не имею, если он повторно использует транзакцию в процессе, я мог бы представить, что это будет, но также я мог бы предположить, что это не будет (если вы, например, подключитесь к другому db, это Должен создать новый) –

4

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

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

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

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

+0

Нет индекса покрытия, но хороший индекс покрытия включает в себя все столбцы, которые должны быть возвращены. Если вы делаете select cola, colb из table1 ... cola и colb должен быть в индексе покрытия, поэтому SQL Server не должен возвращаться к исходной строке для получения данных. –

0

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

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

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

Это, как говорится ...

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

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

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

1

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

Secondo убедитесь, что у вас нет РЕАЛЬНОГО тупика (но это происходит гораздо реже из-за тупика, который может намекнуть).

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

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

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

С уважением
Massimo

+0

Привет Массимо, спасибо за ваш ответ. Бит занят в данный момент, но я скоро вернусь к нему и вернусь к вам. – Brad

0

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

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

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

0

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

delete from part_data where part_id = @P0

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

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

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

    или

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

    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

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