我一直致力于通过多线程创建对象来提高数据库的安装速度(包含数千个对象)。这导致了导致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'myProc2') is not null
BEGIN
PRINT N'Dropping procedure myProc2 ...'
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'myProc') is not null
BEGIN
PRINT N'Dropping procedure myProc ...'
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 :(得分: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取出的锁时,我看到了:
当我看到锁定采取了CREATE PROC时,我看到了:
当我看到ALTER PROC拍摄的锁时,我看到了:
所以我相信你当前的死锁与访问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表用于存储存储过程(没有双关语),似乎对该表的访问非常糟糕。我建议你在一个线程上创建数据库结构,然后用多个线程上的数据小心地填充它。