Sql server Drop过程语句上的死锁
我一直致力于通过多线程创建对象来提高数据库(有数千个对象)的安装速度。这导致了在Sql server Drop过程语句上的死锁,sql-server,deadlock,sql-server-2008-r2,Sql Server,Deadlock,Sql Server 2008 R2,我一直致力于通过多线程创建对象来提高数据库(有数千个对象)的安装速度。这导致了在DROP过程语句上造成死锁的不幸行为 单线程部署花费了很长的时间(因为我们讨论的是很多数据库对象。由于模式安装在几百个客户端上,所以改变模式不是一件容易的事)。缓慢的部署阻碍了我们的开发/发布周期 这些脚本包含以下代码 IF OBJECT_ID(N'myProc') IS NOT NULL BEGIN DROP PROCEDURE myProc END GO CREATE PROC.... 第二个脚
DROP过程
语句上造成死锁的不幸行为
单线程部署花费了很长的时间(因为我们讨论的是很多数据库对象。由于模式安装在几百个客户端上,所以改变模式不是一件容易的事)。缓慢的部署阻碍了我们的开发/发布周期
这些脚本包含以下代码
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>
删除程序myProc2
如果对象id(N&apos;myProc2&apos;)不为空
开始
打印N&apos;删除程序myProc2…&apos;
删除程序myProc2
结束
删除程序myProc
如果对象id(N&apos;myProc&apos;)不为空
开始
打印N&apos;删除程序myProc…&apos;
删除程序myProc
结束
由于sysobjects表用于存储存储过程(没有双关语),因此对该表的访问似乎非常糟糕。我建议您在一个线程上创建数据库结构,然后小心地用多个线程上的数据填充它。防止死锁是书架上的一个主题,但作为一个起点:创建一个将对象与其依赖项相关联的多字典。如果您在部署应用程序中使用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;
}
var dependencies=new Dictionary();//我建议您编写一个多词典类来涵盖这样的情况,我发现它非常有用
book OKToCreateSproc(字符串存储过程名称)
{
foreach(依赖项[sprocName]中的字符串依赖项)
if(createdObjects.Contains(dependency)==false)
返回false;
返回true;
}
请注意,您需要一个线程安全的集合,而且我不相信香草通用字典是安全的。看起来这里已经很好地解决了这个问题:
如果您觉得自己很聪明,您可以通过删除DDL脚本以编程方式填充依赖项
,但这可能有些过分,除非您有一个非常复杂的数据库
哦,是的,您也可以捕获死锁,将问题存储过程推到队列的末尾,然后稍后再试。粗糙,但有效 通过检查删除、创建或更改过程的不同命令所取出的锁,我相信您可以通过将使用的模式更改为:
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\u type=“METADATA.AUDIT\u ACTIONS”和resource\u type=“METADATA.PERMISSIONS”上被解除
- Sch-S锁在存储过程所指的表上取出
- 所有其他锁都是系统对象上的X或IX锁
- 在程序本身上取出Sch-M锁
- 在存储过程所指的表上短暂取出Sch-S锁(发布时引用了
referencemyProc
,反之亦然?@Daniel这些程序完全不相关。死锁在myProc2
上,并且Sch-M
锁。对象创建非常慢,是一个瓶颈。这是多线程it的主要驱动因素。因此,如果它们通过Sch-S
删除一个会导致另一个被sysdependens>关联,你会这么说
锁定?啊,实际上可能是另一种情况。他们已经有SCH-S
锁定,正在等待SCH-M
锁定。可能在添加依赖项信息时会被删除?+1当我试图用由于某些原因,并发事务中的h依赖项还没有完全解决死锁问题。OP表示SP是不相关的,因此不太可能通过依赖项发生冲突。捕获和重试是当前的解决方案。但我对此不太满意,因为这会严重污染构建历史。更改过程的模式e drop/create将解决最常见的死锁情况。我将编写一些代码来更改所有的proc脚本并进行测试。感谢您给出明确的答案。(测试后将接受)我们在1300个表上部署了大约12000个过程,例如,在一台20核的服务器上,这将导致一个CPU在很长一段时间内保持100%。多线程将大大缩短此部署时间。(当我设法获得稳定的部署时,我将发布统计信息。)RE:“存储过程所指的对象上没有取出Sch-S锁”根据我的回答,我看不到与您相同的行为。Martin--我在测试引用不同表的进程的情况。根据Filip的问题,在这种情况下,进程是完全不相关的。我可以将我的回答更改为具体的。@Kendra-你知道OP中的死锁图中的资源在元数据上时的样子吗资源?SCH-S