Drop过程语句的死锁

时间:2011-04-28 14:56:50

标签: sql-server deadlock sql-server-2008-r2

我一直致力于通过多线程创建对象来提高数据库的安装速度(包含数千个对象)。这导致了导致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 个答案:

答案 0 :(得分:8)

通过检查由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取出的锁时,我看到了:

  • 在resource_type =“METADATA.AUDIT_ACTIONS”和resource_type =“METADATA.PERMISSIONS”中取出Sch-M锁
  • SchrocS锁定在sproc引用的表格中取出
  • 系统对象上的所有其他锁定为X或IX

当我看到锁定采取了CREATE PROC时,我看到了:

  • 程序本身取出了Sch-M锁
  • 在sproc引用的表格上简单地取出一个Sch-S锁(并发布&lt; - 更正)
  • 系统对象上的所有其他锁定为X或IX

当我看到ALTER PROC拍摄的锁时,我看到了:

  • 程序本身取出了Sch-M锁
  • Sch-S锁定在表格中取出之前编译的sproc版本(如果只是在新版本中将被简单取出)
  • 系统对象上的所有其他锁定为X或IX

所以我相信你当前的死锁与访问MetaData资源有关,可以通过切换到ALTER模式来缓解这种情况。

然而,Sch-M和Sch-S锁仍然会以不同的方式发挥作用 - 如果你确实有其他相互引用的sprocs,那么仍然可以实现不同的死锁。

补充评论:我很想知道为什么对象创建需要花费这么长时间。除了死锁之外 - 它实际上是在创建占用时间的存储过程吗?我的猜测是问题与表创建和填充有关,我想确保你配置了即时文件初始化,正确配置了数据文件增长设置,设置了恢复模型和/或日志备份,并且只是等待文件增长并在脚本运行时将其清零。

答案 1 :(得分:1)

防止死锁是填充书架的主题,但作为一个起点:创建一个将对象与其依赖关系相关联的多字典。如果您使用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;
  }

请注意,您需要一个线程安全的集合,我不相信vanilla泛型字典是安全的。看起来这里已经很好地解决了这个问题:What's the best way of implementing a thread-safe Dictionary?

如果你感觉很聪明,你可以通过抓取你的DDL脚本以编程方式填充dependencies,但除非你有一个非常复杂的数据库,否则这可能是过度的。

哦,是的,你也可以抓住死锁,将问题sproc推到队列的末尾,稍后再试一次。原油,但有效!

答案 2 :(得分:-1)

由于sysobjects表用于存储存储过程(没有双关语),似乎对该表的访问非常糟糕。我建议你在一个线程上创建数据库结构,然后用多个线程上的数据小心地填充它。