Тупики при операциях отбрасывания - PullRequest
2 голосов
/ 28 апреля 2011

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

Однопоточное развертывание занимало очень много времени (поскольку речь идет о большом количестве объектов базы данных.что-то сделать легко, поскольку схема установлена ​​на нескольких сотнях клиентов.).Медленное развертывание сдерживает наш цикл разработки / выпуска.

Сценарии содержат следующий код

IF OBJECT_ID(N'myProc') IS NOT NULL
  BEGIN
    DROP PROCEDURE myProc
  END
GO
CREATE PROC....

А второй сценарий содержит

IF OBJECT_ID(N'myProc2') IS NOT NULL
  BEGIN
    DROP PROCEDURE myProc2
  END
GO
CREATE PROC....

Эти процедуры совершенно не связаны.Никаких зависимостей, что так каждый.

График взаимоблокировки можно увидеть ниже:

<deadlock-list>
 <deadlock victim="process6c3dc8">
  <process-list>
   <process id="process6c3dc8" taskpriority="0" logused="884" waitresource="OBJECT: 25:1949249999:0 " waittime="3834" ownerId="3008593" transactionname="DROPOBJ" lasttranstarted="2011-04-28T16:34:31.503" XDES="0xa882b950" lockMode="Sch-S" schedulerid="3" kpid="2588" status="suspended" spid="59" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2011-04-28T16:34:31.503" lastbatchcompleted="2011-04-28T16:34:31.503" clientapp=".Net SqlClient Data Provider" hostname="myPc" hostpid="7296" loginname="myLogin" isolationlevel="read committed (2)" xactid="3008593" currentdb="25" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="4" stmtstart="264" stmtend="352" sqlhandle="0x0200000092ebe0126e0f90268e2a5bf1eaba70a098515cd9">
DROP PROCEDURE myProc2     </frame>
    </executionStack>
    <inputbuf>
IF object_id(N&apos;myProc2&apos;) is not null
BEGIN
  PRINT N&apos;Dropping procedure myProc2 ...&apos;
  DROP PROCEDURE myProc2
END    </inputbuf>
   </process>
   <process id="processaa4242c8" taskpriority="0" logused="5800" waitresource="OBJECT: 25:1965250056:0 " waittime="3834" ownerId="3008596" transactionname="DROPOBJ" lasttranstarted="2011-04-28T16:34:31.503" XDES="0xab493950" lockMode="Sch-S" schedulerid="2" kpid="5768" status="suspended" spid="60" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2011-04-28T16:34:31.503" lastbatchcompleted="2011-04-28T16:34:31.503" clientapp=".Net SqlClient Data Provider" hostname="myPC" hostpid="8296" loginname="myLogin" isolationlevel="read committed (2)" xactid="3008596" currentdb="25" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="4" stmtstart="276" stmtend="370" sqlhandle="0x02000000f019293427b8052cc3d5d18be886f958c4b750a1">
DROP PROCEDURE myProc     </frame>
    </executionStack>
    <inputbuf>
IF object_id(N&apos;myProc&apos;) is not null
BEGIN
  PRINT N&apos;Dropping procedure myProc ...&apos;
  DROP PROCEDURE myProc
END    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <objectlock lockPartition="0" objid="1949249999" subresource="FULL" dbid="25" objectname="1949249999" id="lock87308e00" mode="Sch-M" associatedObjectId="1949249999">
    <owner-list>
     <owner id="processaa4242c8" mode="Sch-M"/>
    </owner-list>
    <waiter-list>
     <waiter id="process6c3dc8" mode="Sch-S" requestType="wait"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1965250056" subresource="FULL" dbid="25" objectname="myDatabase.dbo.myProc2" id="lock878d9e80" mode="Sch-M" associatedObjectId="1965250056">
    <owner-list>
     <owner id="process6c3dc8" mode="Sch-M"/>
    </owner-list>
    <waiter-list>
     <waiter id="processaa4242c8" mode="Sch-S" requestType="wait"/>
    </waiter-list>
   </objectlock>
  </resource-list>
 </deadlock>
</deadlock-list>

Ответы [ 3 ]

8 голосов
/ 29 апреля 2011

Из изучения блокировок, снятых различными командами, выполняемыми процедурами DROP, CREATE или ALTER, я полагаю, что вы можете решить вашу конкретную проблему, изменив используемый вами шаблон:

IF OBJECT_ID(N'myProc') IS NULL
    EXEC sp_executesql N'Create Proc myProc as RETURN 0'
GO
ALTER PROC myProc 
AS ...

Когда я смотрю наблокировки, снятые DROP PROC, я вижу, что:

  • блокировки Sch-M снимаются для resource_type = "METADATA.AUDIT_ACTIONS" и resource_type = "METADATA.PERMISSIONS"
  • Блокировка Sch-S снята на столе, к которому относится sproc
  • все остальные блокировки - X или IX на системных объектах

Когда я смотрю на блокировки, взятые CREATE PROCЯ вижу, что:

  • Блокировка Sch-M снята на самой процедуре
  • Блокировка Sch-S кратко снята на столе, к которому относится sproc (ивыпущено <- исправлено) </li>
  • все другие блокировки - это X или IX для системных объектов

Когда я смотрю на блокировки, взятые ALTER PROC, я вижу, что:

  • Блокировка Sch-M снята на самой процедуре
  • AnБлокировка Sch-S снята на столе с ранее скомпилированной версией упомянутого sproc (и если только в новой версии будет кратко снята)
  • все другие блокировки - X или IX на системных объектах

Поэтому я считаю, что ваши текущие взаимоблокировки связаны с доступом к ресурсам MetaData, и это можно было бы уменьшить, переключившись на шаблон ALTER.

Однако Sch-M и Sch-Замки S будут по-прежнему в игре, просто по-другому - поэтому возможны разные тупики, если у вас есть другие спроки, которые ссылаются друг на друга.

Дополнительный комментарий: Iбыло бы интересно узнать, почему создание объекта занимает так много времени в целом.Помимо тупиков - действительно ли это создает хранимые процедуры, которые занимают время?Я полагаю, что проблема связана с созданием и заполнением таблиц, и я хотел бы убедиться, что у вас настроена Мгновенная инициализация файла, правильно настроены параметры роста файла данных, настроена модель восстановления и / или резервного копирования журналов, и нетпросто ждем, пока файлы вырастут и обнулятся, когда ваши скрипты работают.

1 голос
/ 28 апреля 2011

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

var  dependencies = new Dictionary<string, HashSet<string>>();  // I recommend that you write a MultiDictionary class to cover situations like this, I've found it very useful

book OKToCreateSproc(string sprocName)
  {
  foreach (string dependency in dependencies[sprocName])
    if (createdObjects.Contains(dependency) == false)
      return false;
  return true;
  }

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

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

О, да, вы также можете просто поймать взаимоблокировки, перенести проблемный спрок в конец очереди и повторить попытку позже. Грубый, но эффективный!

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

Поскольку таблица sysobjects используется для хранения хранимых процедур (без каламбура), доступ к этой таблице кажется довольно плохим.Я бы предложил вам создать структуру базы данных в одном потоке, а затем тщательно заполнить ее данными в нескольких потоках.

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