我一直在尝试解决死锁问题,并且已经通过SQL Server Profiler捕获了XML死锁图(探查器和有问题的SQL Server均为2014)。我以前必须做过这样的事情,但是这确实使我陷入了困境,因为XML死锁信息仅列出了一个进程……该进程被选为受害者。这是带有表格/列名称的XML:
<deadlock-list>
<deadlock victim="process47b021c28">
<process-list>
<process id="process47b021c28" taskpriority="0" logused="0" waitresource="METADATA: database_id = 28 USER_TYPE(user_type_id = 261), lockPartitionId = 0" waittime="1267" ownerId="6009456" transactionname="@equipmentIDs" lasttranstarted="2018-08-06T10:37:58.643" XDES="0x3a53a63b0" lockMode="Sch-S" schedulerid="2" kpid="10512" status="suspended" spid="55" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2018-08-06T10:37:58.607" lastbatchcompleted="2018-08-06T10:37:58.597" lastattention="1900-01-01T00:00:00.597" clientapp=".Net SqlClient Data Provider" hostname="(scrubbed)" hostpid="15300" loginname="(scrubbed)" isolationlevel="read committed (2)" xactid="5845226" currentdb="28" lockTimeout="4294967295" clientoption1="671219744" clientoption2="128056">
<executionStack>
<frame procname="unknown" line="68" stmtstart="-1" sqlhandle="0x03001c00063d8761a839af0034a9000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
<frame procname="adhoc" line="5" stmtstart="436" stmtend="1352" sqlhandle="0x02000000466dca27e95c8209c65527d104fd85037ea1b3520000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
declare @internalGuid uniqueidentifier = N'00000001-0000-0000-0000-000000000000';
declare @now datetime2 = getdate();
insert into
MyTable (
[id],
[cbr],
[created],
[mbr],
[modified],
[value1],
[value2],
[latitude],
[longitude],
[value3],
[indicator]
)
select
[id],
@internalGuid,
@now,
@internalGuid,
@now,
[value1],
[value2],
[latitude],
[longitude],
[value3],
1
from
MyOtherTable; </inputbuf>
</process>
</process-list>
<resource-list>
<metadatalock subresource="USER_TYPE" classid="user_type_id = 261" dbid="28" lockPartition="0" id="lock3a4c33a80" mode="Sch-M">
<owner-list>
<owner id="process47b021c28" mode="Sch-M"/>
<owner id="process47b021c28" mode="Sch-S" requestType="wait"/>
</owner-list>
<waiter-list>
<waiter id="process47b021c28" mode="Sch-S" requestType="wait"/>
</waiter-list>
</metadatalock>
</resource-list>
</deadlock>
</deadlock-list>
user_type_id 261对应于我的一种用户定义类型,该锁似乎在该类型本身上,除非我误解了这种锁(完全可能)。如果真是这样,我认为这与死锁数据中仅显示一个进程有关。请注意,死锁数据中显示的查询是对具有插入触发器的表的插入...,并且该触发器使用用户定义的类型。
编辑(2018-08-06):这是触发代码的紧急清理版本:
create trigger [dbo].[TR_MyTable_UpdateDerefTable]
on [dbo].[MyTable]
after insert, update, delete
as
begin
set nocount on
declare @myIDs ObjectIDType; -- ObjectIDType is a user-define table type
insert into @myIDs
select i.guidItemId
from inserted i inner join DerefTable ri on i.value1 = ri.guidItemId
union
select d.guidItemId
from deleted d inner join DerefTable ri on d.value = ri.guidItemId;
declare
@dataTypeTL int = 1,
@dataTypeCML int = 2,
@dataTypeDL int = 5;
-- Update dereferenced information, if applicable
if exists(select top 1 1 from @myIDs)
begin
update ri set
[DerefValue] = eq_loc.value2,
[DerefValueSource] = case
when eq_loc.IsFromSourceTL = 1 then @dataTypeTL
when eq_loc.IsFromSourceCML = 1 then @dataTypeCML
when eq_loc.IsFromSourceDL = 1 then @dataTypeDL
else NULL
end,
[DerefValueDescription] = lri.itemName,
[DerefValueType] = lri.itemType,
[DerefValueCategory] = lri.itemTypeCategory,
[DerefValueBU] = lri.buId
from
@myIDs e
left join MyTable eq_loc
on eq_loc.id = (
select top 1 l.id
from MyTable as l
where l.value1 = e.guidItemId
order by l.[Timestamp] desc
)
left join DerefTable ri on e.guidItemId = ri.DisplayguidId
left join DerefTable lri on eq_loc.value2 = lri.guidItemId
end
set nocount off
end;
编辑(2018-08-06):为了使这个问题更清楚,因为周围存在太多的背景信息,这对我来说很难发布,这就是我希望对以下内容有更好的理解:死锁图XML仅列出单个进程的某些情况下的示例?除了删除用户定义的类型外,哪些操作会声明对用户定义的类型的锁定?就我而言,假设这是用户定义的表类型,那么XML信息是否可能误导我以为类型对象本身已被锁定,而实际上该表类型的 instance 具有锁定? (在那最后一种情况下,我认为列出的单个进程本身会死锁)
编辑(2018-08-07):我缩小了锁的另一端,它与用户定义类型本身的删除和创建有关。符号似乎指向表类型定义本身上的锁,而不是该类型实例中的数据上的锁。如果您还记得的话,这是在架构升级路径的上下文中。在某一时刻,将删除此用户定义的表类型(ID为261,如死锁图中所示),并使用附加的列和索引重新创建。从那里开始,在命中触发死锁的语句(被选为受害者)之前,还要处理数十个其他语句(组成许多批次),跨越两个后续版本步骤。中间的语句花了足够的时间,如果这是一个时间问题,我会感到惊讶,但这是我的理解开始下降的地方。我不知道在MSSQL中锁定用户定义类型会发生什么,可以这么说。我当然可以理解为什么受害者进程中的语句依赖于该用户定义类型的重新定义...但是我还不了解的是,可能会导致死锁情况的其他依赖关系会增加。如果我准备好重现场景,以便事先手动更新用户定义的类型,然后在升级过程中为其注释掉drop / create语句,则整个过程将按预期运行并成功完成。