我在SQL Server 2008 sp1上遇到查询通知问题。我有一个表_sys_Events,多个编写器将条目写入,多个读者使用查询通知执行SELECT语句以立即获取最新条目(这是通过.Net System.Data.SqlClient.SqlDependency类完成的)。我们的数据库使用READ_COMMITTED_SNAPSHOT ON运行。我们还安装了累积更新包9,声称包含此问题的修复程序(kb / 975090)。我们得到的是读者和作家之间的僵局。
典型的死锁如下:
<deadlock-list>
<deadlock victim="processb726a508">
<process-list>
<process id="processb726a508" taskpriority="0" logused="0" waitresource="KEY: 5:72057594588758016 (0d004e5bf730)" waittime="624" ownerId="3426492" transactionname="CQueryScan::BeginNotifXact" lasttranstarted="2010-09-13T14:26:57.267" XDES="0x8079ce90" lockMode="RangeS-U" schedulerid="1" kpid="3260" status="suspended" spid="59" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2010-09-13T14:26:57.267" lastbatchcompleted="2010-09-13T14:26:57.267" clientapp=".Net SqlClient Data Provider" hostname="INETC809" hostpid="1532" loginname="bbuser" isolationlevel="read committed (2)" xactid="3426491" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" sqlhandle="0x020000005659040700b3257e8e06defba3179cc01ffb7ca2">
(@1 int)SELECT [Id],[EventData],[LogDate] FROM [dbo].[_sysEvents] WHERE [Id]>@1 ORDER BY [Id] ASC </frame>
<frame procname="adhoc" line="1" sqlhandle="0x02000000acf8f33257911a0ea68aae02722e15daa9a60023">
SELECT Id, EventData, LogDate FROM dbo._sysEvents WHERE Id > 1425265 ORDER BY Id </frame>
</executionStack>
<inputbuf>
SELECT Id, EventData, LogDate FROM dbo._sysEvents WHERE Id > 1425265 ORDER BY Id </inputbuf>
</process>
<process id="process8db45b88" taskpriority="0" logused="8348" waitresource="KEY: 5:72057594588758016 (1200caf8f72f)" waittime="623" ownerId="3424785" transactionname="user_transaction" lasttranstarted="2010-09-13T14:26:47.157" XDES="0xf8906dc0" lockMode="RangeS-U" schedulerid="1" kpid="3656" status="suspended" spid="52" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2010-09-13T14:26:57.530" lastbatchcompleted="2010-09-13T14:26:57.530" clientapp=".Net SqlClient Data Provider" hostname="INETC1012" hostpid="3584" loginname="bbuser" isolationlevel="read committed (2)" xactid="3424785" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="66" stmtend="218" sqlhandle="0x02000000d60d99067d5177738e10de6507ecd187d217792e">
INSERT INTO [dbo].[_sysEvents]([EventData], [LogDate])
VALUES (@p0, @p1) </frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@p0 varbinary(640),@p1 datetime)INSERT INTO [dbo].[_sysEvents]([EventData], [LogDate])
VALUES (@p0, @p1)
SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value] </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594588758016" dbid="5" objectname="BBBets.sys.query_notification_734885935" indexname="cidx" id="lockc6f66d00" mode="RangeX-X" associatedObjectId="72057594588758016">
<owner-list>
<owner id="process8db45b88" mode="RangeX-X"/>
</owner-list>
<waiter-list>
<waiter id="processb726a508" mode="RangeS-U" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594588758016" dbid="5" objectname="BBBets.sys.query_notification_734885935" indexname="cidx" id="lockc7b29380" mode="RangeS-U" associatedObjectId="72057594588758016">
<owner-list>
<owner id="processb726a508" mode="RangeS-U"/>
</owner-list>
<waiter-list>
<waiter id="process8db45b88" mode="RangeS-U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
</deadlock-list>
SELECT语句和INSERT语句都是在默认的READ_COMMITED隔离级别完成的,在我们的例子中使用行版本控制。如您所见,锁定发生在同一索引对象上。我的猜测是行版本允许这种情况发生?
我有什么办法可以解决这个问题吗?也许SELECT语句有不同的隔离级别?我不应该在我的场景中使用Notification Services吗?
提前致谢
答案 0 :(得分:2)
我意识到这是一个非常晚的回复,但万一有人仍然感兴趣...
我从未真正解决过这个问题(我认为这不可能)。但我确实找到了解决方法。我所做的是使用Service Broker。我创建了一个sp,它将消息发送到队列(每个消息代替一个插入 - 这个sp由多个编写器调用)和另一个消耗此队列的sp。第二个sp读取所有挂起的消息(插入),然后使用TABLOCK对sys_Events表进行批量插入(当有消息消耗并在其自己的进程中运行时,SqlSrv会自动调用它。)
所以我实际上正在做的是从单个SqlSrv进程收集插入并同时完成它们。 Service Broker消息和队列是完全可靠的,是数据库的一部分,因此不会丢失数据完整性。如果有的话,这种方法实际上更快,实现起来相当简单。
PS。我仍然认为这种行为是一个错误,有一天应该修复!