我有一張用作簡單更改日誌的表;行需要按照它們寫入的順序從它讀取。該表是這樣的:類似隊列表中的死鎖,SqlDependency混合在
create table PublishedEvent (
PublishedEventId int identity not null,
EntityGuid uniqueidentifier null,
EntityId int null,
EventName nvarchar(100) not null,
Operation nvarchar(10) not null,
Timestamp datetimeoffset not null default(sysdatetimeoffset()),
primary key (PublishedEventId)
);
表被寫入到從內部觸發類似於此:
create trigger Appointment_Insert on Appointment after insert as
begin
insert into PublishedEvent (EntityName, EntityId, Operation)
select 'Appointment', i.AppointmentId, 'insert' from inserted i
end
有很多這樣的觸發器,所以有很多有效的作家。
在應用程序代碼中,PublishedEvent表是通過SqlDependency的實例讀取的。我使用SqlDependency是因爲我想避免輪詢數據庫,並且同時需要在新行到達時作出反應。
using (var conn = new SqlConnection(...))
{
conn.Open();
using (var tr = conn.BeginTransaction(IsolationLevel.ReadCommitted))
{
var cmd = new SqlCommand("select PublishedEventId, EventName, EntityGuid, EntityId, Operation, Timestamp from dbo.PublishedEvent order by PublishedEventId", conn, tr);
var dependency = new SqlDependency(cmd);
dependency.OnChange += HandleDependencyChange;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read()) { ... }
}
}
}
訪問的SqlDependency被序列化,這樣只能有一次一個優秀的查詢。應用程序讀取查詢返回的PublishedEvent行,並處理它們。當然,它也會連接到SqlDependency的OnChange事件,因此循環可以重新開始。
一旦應用程序被處理完PublishedEvent行,它將刪除他們:
using (var conn = new SqlConnection(...))
{
conn.Open();
using (var tr = conn.BeginTransaction(IsolationLevel.ReadUncommitted))
{
var cmd = new SqlCommand("delete from pe from PublishedEvent pe inner join @EventIds ei on ei.Id = pe.PublishedEventId", tr.Connection, tr);
cmd.Parameters.Add(new SqlParameter("@EventIds", ...));
cmd.ExecuteNonQuery();
tr.Commit();
}
}
注:@EventIds是包含先前讀PublishedEvent行的PublishedEventId列的表值參數。
現在,我的問題是,我有時會在數據庫中發生死鎖。 SQL事件探查器告訴我,插入到Appointment表之間發生死鎖(可能從Foo的觸發器中插入PublishedEvent是真正的罪魁禍首)並將其刪除到PublishedEvent表。 我看過網上的相關文章,比如Using tables as Queues,但沒有一個和我有類似的約束。
有沒有避免死鎖的方法,同時有一個解決方案允許多個作者,以及使用SqlDependency的單個閱讀器?
編輯:在@usr的請求下,這裏是一個樣例死鎖圖。正如你將會看到的那樣,SQL比問題中提到的要複雜一些,儘管我不認爲我的部門會改變任何w.r.t.我的問題。
<deadlock-list>
<deadlock victim="process5989b88">
<process-list>
<process id="process5989b88" taskpriority="0" logused="864" waitresource="KEY: 5:72057594076397568 (7860bcaa51f1)" waittime="717" ownerId="8200442" transactionname="DeletePublishedEvents" lasttranstarted="2014-11-08T12:32:54.067" XDES="0x8004b950" lockMode="RangeS-U" schedulerid="2" kpid="9560" status="suspended" spid="56" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-11-08T12:32:54.070" lastbatchcompleted="2014-11-08T12:32:54.067" clientapp="dev-MobileMed Noyau" hostname="PLALONDEW8" hostpid="22392" loginname="PLALONDEW8\dev-MM$Core" isolationlevel="read uncommitted (1)" xactid="8200442" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="82" sqlhandle="0x02000000fac6ff387b191d62a29fa17565e4e54bcc8aa7f5">
delete from pe from PublishedEvent pe inner join @EventIds ei on ei.Id = pe.PublishedEventId </frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@EventIds [dbo].[IntTableType] READONLY)delete from pe from PublishedEvent pe inner join @EventIds ei on ei.Id = pe.PublishedEventId </inputbuf>
</process>
<process id="process59bddc8" taskpriority="0" logused="162844" waitresource="KEY: 5:72057594076397568 (49d0d2cdfe43)" waittime="715" ownerId="8200412" transactionname="user_transaction" lasttranstarted="2014-11-08T12:32:54.060" XDES="0x805d1950" lockMode="RangeS-U" schedulerid="4" kpid="18004" status="suspended" spid="70" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-11-08T12:32:54.067" lastbatchcompleted="2014-11-08T12:32:54.067" clientapp="dev-MMImporter" hostname="PLALONDEW8" hostpid="22012" loginname="PLALONDEW8\dev-MM$Core" isolationlevel="read committed (2)" xactid="8200412" currentdb="5" lockTimeout="4294967295" clientoption1="673316896" clientoption2="128056">
<executionStack>
<frame procname="dev-DmeDB.dbo.ScheduleSlot_InsertUpdate" line="19" stmtstart="1246" stmtend="2066" sqlhandle="0x03000500a888b73a4c40d900dca300000000000000000000">
insert into PublishedEvent (EventName, EntityId, Operation)
select 'ScheduleSlot', i.ScheduleSlotId,
case
when i.IsDeleted = 1 then 'softdelete'
when d.ScheduleSlotId is null then 'insert'
else 'update'
end
from inserted i
left join deleted d on i.ScheduleSlotId = d.ScheduleSlotId; </frame>
<frame procname="dev-DmeDB.medicoadmin.PLAGEHORAIRERV_AIU_REPL" line="10" stmtstart="548" stmtend="1786" sqlhandle="0x03000500bbf3ef51aabcd900dca300000000000000000000">
with cte (ScheduleSlotId, AppointmentTypeId, AppointmentId) as
(
select ss.ScheduleSlotId, at.AppointmentTypeId, a.AppointmentId
from inserted i
inner join dbo.ScheduleSlot ss on ss.MedicoAdminId = i.ID_PLAGEHORAIRE
left join AppointmentType at on at.MedicoAdminId = i.ID_TYPERENDEZVOUS
left join Appointment a on a.MedicoAdminId = i.ID_RENDEZVOUS
)
merge dbo.ScheduleSlot as target
using cte as source on (target.ScheduleSlotId = source.ScheduleSlotId)
when matched then
update set
AppointmentTypeId = source.AppointmentTypeId,
AppointmentId = source.AppointmentId; </frame>
<frame procname="adhoc" line="1" stmtstart="150" sqlhandle="0x02000000938c652d03c75d708e1fa976f91c4da900714d0c">
INSERT INTO [medicoadmin].PLAGEHORAIRERV (ID_PLAGEHORAIRE, ID_TYPERENDEZVOUS, ID_RENDEZVOUS) VALUES (@ID_PLAGEHORAIRE, @ID_TYPERENDEZVOUS, @ID_RENDEZVOUS); </frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@ID_PLAGEHORAIRE int,@ID_TYPERENDEZVOUS int,@ID_RENDEZVOUS nvarchar(4000))INSERT INTO [medicoadmin].PLAGEHORAIRERV (ID_PLAGEHORAIRE, ID_TYPERENDEZVOUS, ID_RENDEZVOUS) VALUES (@ID_PLAGEHORAIRE, @ID_TYPERENDEZVOUS, @ID_RENDEZVOUS); </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594076397568" dbid="5" objectname="dev-DmeDB.sys.query_notification_155199653" indexname="cidx" id="lock88780d00" mode="RangeX-X" associatedObjectId="72057594076397568">
<owner-list>
<owner id="process59bddc8" mode="RangeX-X"/>
</owner-list>
<waiter-list>
<waiter id="process5989b88" mode="RangeS-U" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594076397568" dbid="5" objectname="dev-DmeDB.sys.query_notification_155199653" indexname="cidx" id="lock8f111a80" mode="RangeS-U" associatedObjectId="72057594076397568">
<owner-list>
<owner id="process5989b88" mode="RangeS-U"/>
</owner-list>
<waiter-list>
<waiter id="process59bddc8" mode="RangeS-U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
</deadlock-list>
隊列表上有哪些索引?如果每個密鑰都被插入並刪除一次,則不會發生死鎖。有些事情在這個問題上並不明顯。最多一次事務中有多少次插入和刪除?也許鎖定升級從5000鎖開始。 – usr 2014-11-08 15:23:46
@usr沒有額外的索引。所有與表的交互都顯示在問題中:插入,刪除和通過SqlDependency讀取。 – 2014-11-08 16:50:13
好的。將死鎖圖表作爲XML發佈。 – usr 2014-11-08 17:20:48