Atz*_*oya 13 .net c# deadlock sql-server-2008
我将尽可能详细地解释我的问题,我将不胜感激任何帮助/建议.我的问题是由两个查询(一次插入和一次更新)引起的死锁.我正在使用MS-SQL server 2008
我有两个使用相同数据库的应用程序:
Web应用程序在不使用事务的情况下插入展示记录,而Windows服务应用程序在使用IsolationLevel.ReadUncommitted事务时计算展示次数.Windows服务应用程序中的存储过程执行以下操作:
Windows服务存储过程:
循环通过将isCalculated标志设置为false并且日期<@now的所有展示,增加计数器和连接到展示表的另一个表中的其他数据,并isCalculated在具有日期<@now的展示中将标记设置为true.因为这个存储过程非常大,没有必要粘贴它,这里是proc的缩短代码片段:
DECLARE @nowTime datetime = convert(datetime, @now, 21)
DECLARE dailyCursor CURSOR FOR
SELECT Daily.dailyId,
Daily.spentDaily,
Daily.impressionsCountCache ,
SUM(Impressions.amountCharged) as sumCharged,
COUNT(Impressions.impressionId) as countImpressions
FROM Daily INNER JOIN Impressions on Impressions.dailyId = Daily.dailyId
WHERE Impressions.isCharged=0 AND Impressions.showTime < @nowTime AND Daily.isActive = 1
GROUP BY Daily.dailyId, Daily.spentDaily, Daily.impressionsCountCache
OPEN dailyCursor
DECLARE @dailyId int,
@spentDaily decimal(18,6),
@impressionsCountCache int,
@sumCharged decimal(18,6),
@countImpressions int
FETCH NEXT FROM dailyCursor INTO @dailyId,@spentDaily, @impressionsCountCache, @sumCharged, @countImpressions
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE Daily
SET spentDaily= @spentDaily + @sumCharged,
impressionsCountCache = @impressionsCountCache + @countImpressions
WHERE dailyId = @dailyId
FETCH NEXT FROM dailyCursor INTO @dailyId,@spentDaily, @impressionsCountCache, @sumCharged, @countImpressions
END
CLOSE dailyCursor
DEALLOCATE dailyCursor
UPDATE Impressions
SET isCharged=1
WHERE showTime < @nowTime AND isCharged=0
Run Code Online (Sandbox Code Playgroud)
Web App存储过程:
这个过程非常简单,它只是在表中插入记录.这是一个缩短的代码段:
INSERT INTO Impressions
(dailyId, date, pageUrl,isCalculated) VALUES
(@dailyId, @date, @pageUrl, 0)
Run Code Online (Sandbox Code Playgroud)
代码
调用这些存储过程的代码非常简单,它只是创建传递所需参数并执行它们的SQL命令
//i send the date like this
string date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff",
CultureInfo.InvariantCulture);
SqlCommand comm = sql.StoredProcedureCommand("storedProcName",
parameters, values);
Run Code Online (Sandbox Code Playgroud)
我经常遇到死锁(例外情况发生在Web应用程序中,而不是Windows服务),并且在使用SQL-Profiler之后,我发现死锁可能是因为这两个查询而发生的(我没有分析分析器数据的丰富经验).
从SQL Server Profiler收集的最新跟踪数据可以在此问题的底部找到
理论上,这两个存储过程应该能够一起工作,因为第一个存储过程一个接一个地插入日期= DateTime.Now,第二个计算具有日期<DateTime.Now的印象.
编辑:
这是在Windows服务应用程序中运行的代码:
SQL sql = new SQL();
DateTime endTime = DateTime.Now;
//our custom DAL class that opens a connection
sql.StartTransaction(IsolationLevel.ReadUncommitted);
try
{
List<string> properties = new List<string>() { "now" };
List<string> values = new List<string>() { endTime.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture) };
SqlCommand comm = sql.StoredProcedureCommannd("ChargeImpressions", properties, values);
comm.Transaction = sql.Transaction;
ok = sql.CheckExecute(comm);
}
catch (Exception up)
{
ok = false;
throw up;
}
finally
{
if (ok)
sql.CommitTransaction();
else
sql.RollbackTransactions();
CloseConn();
}
Run Code Online (Sandbox Code Playgroud)
编辑:
我按照Martin Smith的建议添加了两个表的索引,如下所示:
CREATE NONCLUSTERED INDEX [IDX_Daily_DailyId] ON [dbo].[Daily]
(
[daily] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)
和
CREATE NONCLUSTERED INDEX [IDX_Impressions_isCharged_showTime] ON [dbo].[Impressions]
(
[isCharged] ASC,
[showTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)
现在没有例外,将在稍后报告
编辑:
不幸的是,这并没有解决僵局问题.我将在探查器中启动死锁跟踪,以查看死锁是否与以前相同.
编辑:
粘贴新的跟踪(对我来说它看起来和前一个一样),无法捕获执行计划的屏幕(它太大了)但这里是执行计划中的xml.这里是执行的截图插入查询的计划:

<deadlock victim="process14e29e748">
<process-list>
<process id="process14e29e748" taskpriority="0" logused="952" waitresource="KEY: 6:72057594045071360 (f473d6a70892)" waittime="4549" ownerId="2507482845" transactionname="INSERT" lasttranstarted="2011-09-05T11:59:16.587" XDES="0x15bef83b0" lockMode="S" schedulerid="1" kpid="2116" status="suspended" spid="65" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2011-09-05T11:59:16.587" lastbatchcompleted="2011-09-05T11:59:16.587" clientapp=".Net SqlClient Data Provider" hostpid="2200" isolationlevel="snapshot (5)" xactid="2507482845" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="dbo.InsertImpression" line="27" stmtstart="2002" stmtend="2560" sqlhandle="0x03000600550e30512609e200529f00000100000000000000">
INSERT INTO Impressions
(dailyId, languageId, showTime, pageUrl, amountCharged, age, ipAddress, userAgent, portalId, isCharged,isCalculated) VALUES
(@dailyId, @languageId, @showTime, @pageUrl, @amountCharged, @age, @ip, @userAgent, @portalId, 0, 0) </frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 1362103893] </inputbuf>
</process>
<process id="process6c9dc8" taskpriority="0" logused="335684" waitresource="KEY: 6:72057594045464576 (5fcc21780b69)" waittime="4475" ownerId="2507482712" transactionname="transaction_name" lasttranstarted="2011-09-05T11:59:15.737" XDES="0x1772119b0" lockMode="U" schedulerid="2" kpid="3364" status="suspended" spid="88" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2011-09-05T11:59:15.737" lastbatchcompleted="2011-09-05T11:59:15.737" clientapp=".Net SqlClient Data Provider" hostpid="1436" isolationlevel="read uncommitted (1)" xactid="2507482712" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="dbo.ChargeImpressions" line="60" stmtstart="4906" stmtend="5178" sqlhandle="0x03000600e3c5474f0609e200529f00000100000000000000">
UPDATE Impressions
SET isCharged=1
WHERE showTime &lt; @nowTime AND isCharged=0
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 1330103779] </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594045071360" dbid="6" objectname="dbo.Daily" indexname="PK_Daily" id="lock14c6aab00" mode="X" associatedObjectId="72057594045071360">
<owner-list>
<owner id="process6c9dc8" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process14e29e748" mode="S" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594045464576" dbid="6" objectname="dbo.Impressions" indexname="IDX_Impressions_isCharged_showTime" id="lock14c901200" mode="X" associatedObjectId="72057594045464576">
<owner-list>
<owner id="process14e29e748" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process6c9dc8" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Run Code Online (Sandbox Code Playgroud)
编辑:
根据Jonathan Dickinson的建议:
更新:
查询执行时间在最后一次更改后减少,但异常数没有.
希望最后更新:
Martin Smith提出的更改现在正在进行,插入查询现在使用非聚集索引,理论上这应该可以解决问题.现在没有报道任何例外(保持手指交叉)
Daily您的 Windows 服务游标会更新它锁定的各个行X。这些在交易结束之前不会被释放。
然后,您的 Web 应用程序会执行插入操作,并在新插入的行上Impressions保持X锁定,同时等待其他进程锁定的S其中一行上的锁定。Daily它需要读取此内容来验证 FK 约束。
然后,您的 Windows 服务会对其扫描的行Impressions执行更新操作。U没有索引允许它查找行,因此此扫描包括 Web 应用程序添加的行。
所以
(1) 您可以添加一个复合索引,Impressions反之亦然showTime, isCharged(检查执行计划),以允许通过索引查找而不是完整扫描来找到 Windows 服务将更新的行。
-或者
(2) 您可以在 上添加冗余的非聚集索引Daily(DailyId)。这将比聚集索引窄很多,因此 FK 验证可能会使用它,而不是需要锁定S聚集索引行。
编辑
免责声明:以下内容基于假设和观察,而不是我发现的任何记录!
看来想法(2)“按原样”不起作用。执行计划显示,尽管现在有更窄的索引可用,但 FK 验证仍然继续针对聚集索引进行。sys.foreign_keys有列referenced_object_id, key_index_id,我推测验证当前总是在列出的索引上进行,并且查询优化器当前不考虑替代方案,但尚未找到任何记录这一点的内容。
我发现在删除并重新添加外键约束后,查询计划中的相关值 sys.foreign_keys更改为开始使用较窄的索引。
CREATE TABLE Daily(
DailyId INT IDENTITY(1,1) PRIMARY KEY CLUSTERED NOT NULL,
Filler CHAR(4000) NULL,
)
INSERT INTO Daily VALUES ('');
CREATE TABLE Impressions(
ImpressionId INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
DailyId INT NOT NULL CONSTRAINT FK REFERENCES Daily (DailyId),
Filler CHAR(4000) NULL,
)
/*Execution Plan uses clustered index - There is no NCI*/
INSERT INTO Impressions VALUES (1,1)
ALTER TABLE Daily ADD CONSTRAINT
UQ_Daily UNIQUE NONCLUSTERED(DailyId)
/*Execution Plan still use clustered index even after NCI created*/
INSERT INTO Impressions VALUES (1,1)
ALTER TABLE Impressions DROP CONSTRAINT FK
ALTER TABLE Impressions WITH CHECK ADD CONSTRAINT FK FOREIGN KEY(DailyId)
REFERENCES Daily (DailyId)
/*Now Execution Plan now uses non clustered index*/
INSERT INTO Impressions VALUES (1,1)
Run Code Online (Sandbox Code Playgroud)