2011-04-28 123 views
2

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

'myProc'是否引用'myProc2',反之亦然? – 2011-04-28 15:01:15

+0

@Daniel特效是完全不相关的。 – 2011-04-28 18:14:00

回答

8

从检查由不同的命令取出锁是中删除,创建或更改程序,我相信你可以改变你使用的模式解决您的具体问题:

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

当我看我发现:

  • Sch-M锁取出在resource_type =“METADATA.AUDIT_ACTIONS”和resource_type =“METADATA。权限”
  • SCH-S锁取出一个表上存储过程是指
  • 所有其它锁是X或IX上系统对象

当我看截取的CREATE PROC的锁,我看出:

  • 的SCH-M锁取出的过程本身
  • 的SCH-S锁取出简要地在表上存储过程是指(和释放< - 校正)
  • 所有其它锁是X或IX上系统对象

当我看由ALTER PROC所采取的锁,我看到:

  • 的SCH-M锁取出上程序本身
  • 一个Sch-S锁取出在一张表上,前面编译的sproc参考的版本(如果仅在新版本中将被简单地取出)
  • 所有其他锁都是X或IX系统对象

所以我相信你目前的僵局与访问MetaData资源有关,这可以通过切换到ALTER模式来缓解。

但是,Sch-M和Sch-S锁仍然会以不同的方式发挥作用 - 如果您还有其他相互引用的sprocs,那么不同的死锁仍然是可能的。

附加说明:我会好奇地想知道为什么对象创建需要这么长时间。除了死锁之外 - 它实际上是否正在创建花费时间的存储过程?我的猜测是,这个问题与表创建和人口有关,我想确保你配置了即时文件初始化,数据文件增长设置配置正确,恢复模式和/或日志备份设置,并且aren只是等待脚本运行时文件增长并清零。

+1

更改过程drop/create的模式将解决最常见的死锁情况。我将编写一些代码来更改所有proc脚本并对其进行测试。感谢您的明确答案。 (将在测试后接受) – 2011-04-28 20:40:34

+0

我们正在1300个表上部署约12000个程序,例如20核心服务器,导致1个CPU长时间处于100%。多线程减少了很多部署时间。(当我设法稳定部署时,我会发布统计信息。) – 2011-04-28 21:05:53

+0

RE:“一个Sch-S锁不会取出在一个sproc所指的对象上”我没有看到和我一样的行为。 – 2011-04-29 14:07:23

-1

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

+0

死锁发生在'Sch-M'和'Sch-S'锁上。 – 2011-04-28 15:07:06

+0

对象创建非常缓慢而且是一个瓶颈。这是多线程的主要驱动。 – 2011-04-28 18:20:14

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; 
    } 

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

如果你感觉很聪明,你可以通过编写你的DDL脚本来编程填充dependencies,但这可能是过度杀毒,除非你有一个非常复杂的数据库。

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

+0

所以你在说如果他们通过'sysdepends'关联,那么放下一个将会在另一个上使用'SCH-S'锁定? – 2011-04-28 15:26:03

+0

啊其实可能是其他方式。他们已经拥有SCH-M锁,并正在等待SCH-S锁。也许这在添加依赖信息时被取消了? – 2011-04-28 15:53:42

+0

+1当我尝试在并发事务中创建具有依赖关系的过程时,我可以肯定会阻止,但由于某种原因,并未完全设法导致死锁。 – 2011-04-28 16:31:59