2010-05-31 41 views
5

我在我的大型Web应用程序中收到很多死锁。我有关于死锁的数据,但我不明白他们为什么会发生

How to automatically re-run deadlocked transaction? (ASP.NET MVC/SQL Server)

在这里,我想重新运行僵持交易,但有人告诉我,摆脱僵局的 - 这是好多了,比试图赶上死锁。

所以我花了整整一天的时间与SQL事件探查器,设置跟踪键等。这就是我得到的。

有一个Users表。我有以下查询非常高的可用的页面(这不是唯一的查询,但它是引起麻烦的)

UPDATE Users 
SET views = views + 1 
WHERE ID IN (SELECT AuthorID FROM Articles WHERE ArticleID = @ArticleID) 

再有就是在以下查询ALL网页:

User = DB.Users.SingleOrDefault(u => u.Password == password && u.Name == username); 

这就是我从cookies获取用户的地方。

通常会发生死锁,并选择第二个Linq-to-SQL查询作为受害者,因此它不会运行,并且我的站点的用户会看到错误屏幕。

这是由SQL事件探查器捕获的.XDL图信息(这只是第一僵局,这不是唯一的一个,整个列表是巨大的。):

<deadlock-list> 
    <deadlock victim="process824df048"> 
     <process-list> 
      <process id="process824df048" taskpriority="0" logused="0" waitresource="PAGE: 7:1:13921" waittime="1830" ownerId="91418" transactionname="SELECT" lasttranstarted="2010-05-31T12:17:37.663" XDES="0x868175e0" lockMode="S" schedulerid="2" kpid="5076" status="suspended" spid="72" sbid="0" ecid="2" priority="0" trancount="0" lastbatchstarted="2010-05-31T12:17:37.663" lastbatchcompleted="2010-05-31T12:17:37.663" clientapp=".Net SqlClient Data Provider" hostname="WIN-S41KV2CLS67" hostpid="6920" isolationlevel="read committed (2)" xactid="91418" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
       <executionStack> 
        <frame procname="adhoc" line="1" stmtstart="74" sqlhandle="0x02000000de1cb30b5b2e40e31ffb345af3c7529430b559c2"> 
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  </frame> 
        <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000"> 
unknown  </frame> 
       </executionStack> 
       <inputbuf> 
       </inputbuf> 
      </process> 
      <process id="process8765fb88" taskpriority="0" logused="216" waitresource="PAGE: 7:1:14196" waittime="1822" ownerId="91408" transactionname="UPDATE" lasttranstarted="2010-05-31T12:17:37.640" XDES="0x86978e90" lockMode="IX" schedulerid="2" kpid="5216" status="suspended" spid="73" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2010-05-31T12:17:37.557" lastbatchcompleted="2010-05-31T12:17:37.557" clientapp=".Net SqlClient Data Provider" hostname="WIN-S41KV2CLS67" hostpid="6920" loginname="sdfkj93jks9sl" isolationlevel="read committed (2)" xactid="91408" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
       <executionStack> 
        <frame procname="database.dbo.UpdateUserStats" line="31" stmtstart="1794" stmtend="2088" sqlhandle="0x03000700bac8836333e58f00879d00000100000000000000"> 
UPDATE Users 
    SET Views = Views + 1 
    WHERE ID IN (SELECT AuthorID FROM Articles WHERE ArticleID = @ArticleID)  </frame> 
        <frame procname="adhoc" line="1" stmtstart="84" sqlhandle="0x01000700b7c78e0760dd3f81000000000000000000000000"> 
EXEC @RETURN_VALUE = [dbo].[UpdateUserStats] @UserID = @p0 </frame> 
        <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000"> 
unknown  </frame> 
       </executionStack> 
       <inputbuf> 
(@p0 int,@RETURN_VALUE int output)EXEC @RETURN_VALUE = [dbo].[UpdateUserStats] @UserID = @p0 </inputbuf> 
      </process> 
      <process id="process86ce0988" taskpriority="0" logused="10000" waittime="1806" schedulerid="1" kpid="2604" status="suspended" spid="72" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2010-05-31T12:17:37.663" lastbatchcompleted="2010-05-31T12:17:37.663" clientapp=".Net SqlClient Data Provider" hostname="WIN-S41KV2CLS67" hostpid="6920" loginname="sdfkj93jks9sl" isolationlevel="read committed (2)" xactid="91418" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
       <executionStack> 
        <frame procname="adhoc" line="1" stmtstart="74" sqlhandle="0x02000000de1cb30b5b2e40e31ffb345af3c7529430b559c2"> 
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  </frame> 
        <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000"> 
unknown  </frame> 
       </executionStack> 
       <inputbuf> 
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- </inputbuf> 
      </process> 
     </process-list> 
     <resource-list> 
      <pagelock fileid="1" pageid="13921" dbid="7" objectname="database.dbo.Users" id="lock85535c80" mode="IX" associatedObjectId="72057594046382080"> 
       <owner-list> 
        <owner id="process8765fb88" mode="IX"/> 
       </owner-list> 
       <waiter-list> 
        <waiter id="process824df048" mode="S" requestType="wait"/> 
       </waiter-list> 
      </pagelock> 
      <pagelock fileid="1" pageid="14196" dbid="7" objectname="database.dbo.Users" id="lock8469f980" mode="SIU" associatedObjectId="72057594046382080"> 
       <owner-list> 
        <owner id="process86ce0988" mode="S"/> 
       </owner-list> 
       <waiter-list> 
        <waiter id="process8765fb88" mode="IX" requestType="convert"/> 
       </waiter-list> 
      </pagelock> 
      <exchangeEvent id="Pipe894b0680" WaitType="e_waitPipeGetRow" nodeId="0"> 
       <owner-list> 
        <owner id="process824df048"/> 
       </owner-list> 
       <waiter-list> 
        <waiter id="process86ce0988"/> 
       </waiter-list> 
      </exchangeEvent> 
     </resource-list> 
    </deadlock> 

我读了很多关于死锁......我不明白为什么这会导致僵局。

所以很明显,这两个查询运行非常频繁。至少每秒一次。也许更经常(300-400个用户在线)。所以他们可以很容易地同时运行,但为什么会导致死锁?请帮忙。

谢谢

+1

你在SQL2005或更高版本?如果是的话,你从SQL Profiler中获得死锁图表?你也知道你的查询在什么事务隔离级别下运行吗? – 2010-05-31 17:57:36

+0

是的,我得到了图。这是我从中获得这些信息的地方。我不知道有关事务隔离的任何信息。我在哪里可以检查它? – Alex 2010-05-31 18:33:05

+1

在剖析器跟踪中,右键单击死锁图事件并选择提取事件数据,将其另存为xml。然后打开记事本,并找到“isolationlevel” – 2010-05-31 18:53:44

回答

11

您需要捕获死锁图。附加Profiler并捕获Deadlock Graph Event类。保存.XDL图并将该信息添加到您的帖子中。

在此之前,很明显你的DB.Users。的SingleOrDefault查询至少需要,如果不是名和密码的名称索引:

CREATE INDEX idxUsersNamePassword on Users(Name,Password); 

我希望用户已经拥有ID的指标,文章对条款ArticleID覆盖的AuthorID太多的索引。假设Users.ID和Articles.ArticleID是它们各自表中的PK,它们可能是相应的集群键,因此它是真实的。但值得仔细检查。

而且,正如我已经在你以前的帖子回答你,一旦你决定继续前进,并留下未回答,您应该考虑启用Snapshot Isolation

ALTER DATABASE ... SET READ_COMMITTED_SNAPSHOT ON 

除此之外,存储明文密码一个重大的#fail。

后更新僵局信息

有三个过程(请求):

  • A),其运行SELECT ... FROM Users WHERE Password = ... and Name = ...
  • 乙... F048)...... 0988这是运行SELECT ... FROM Users WHERE Password = ... and Name = ...
  • C)... FB88正在运行的UPDATE ...

死锁循环是:

  1. C对页IX锁等待,由A公司的S锁定页S锁
  2. 乙等待被阻塞,被C的IX锁阻塞
  3. 甲平行等待交换资源,被封锁B

因此,循环是C→A-> B-> C。

从这两个SELECT涉及的事实决定:1)使用并行计划和2)使用页面锁显然是他们做了整个Users表的端到端扫描。所以问题是,正如我预测的那样,缺少用户上的(名称,密码)索引,导致查询扫描太多的数据。添加索引会将SELECT转换为Nc索引中的直接SEEK,并对Clustered索引进行查找,这将显着减少与UPDATE重叠的窗口。现在UPDATE几乎保证与所有冲突发生冲突,因为每个SELECT都保证读取每一行。

添加索引将解决眼前的问题。使用快照隔离将掩码问题,因为除非添加(名称,密码)索引,否则端到端扫描仍将发生。或者只有(名称)也可能起作用。

对于未来的可扩展性,更新每个页面视图上的视图列将不起作用。延迟更新,批量聚合计数更新,垂直分区Users表和取出Views列是可行的选择。

+1

对不起,我忘了接受你的回答。是的,我将要更新密码存储系统。 我会用.XDL信息更新我的问题。 – Alex 2010-05-31 19:15:47

+0

.XDL信息已过帐。 – Alex 2010-05-31 19:28:15

+0

非常感谢您提供这样详细的答案! 我从来没有遇到索引与主键不同的任何其他列。我需要做什么来添加(名称,密码)索引? 我在表格的设计者中找到了索引/键盘窗口。它有一个PK_Users键,我可以为用户名添加IX_Users键,但我怎样才能将它与密码关联? 再次感谢您。 – Alex 2010-05-31 20:42:41

1

你的问题有很多相似之处的与这里Diagnosing Deadlocks in SQL Server 2005

(LINQ到SQL,只读事务由读写交易正在僵持)

如果你是SQL2005或者稍后可能设置快照隔离,就像在该线程中讨论的那样将完成这项工作。否则,请更新您的帖子,详细介绍您使用的版本。

+0

谢谢,我会看看。 – Alex 2010-05-31 18:32:09

1

在这种情况下(即您正在阅读的数据的类型以及在该数据上发生的更新的性质),我会在读取未提交隔离时运行用户查找查询。

或者,更多参与的变化。从你发布的描述中,我会考虑不保留用户记录中的视图。相反,我会在文章中记录ViewCount,然后通过AuthorID从Articles.ViewCount总和中调整用户的总视图。

相关问题