使用Linq2Sql的流氓事务边界

时间:2012-07-12 14:50:28

标签: c# sql-server sql-server-2008 linq-to-sql

我有一个在服务器端使用Linq2Sql的网站。

每隔一段时间(尚未确定触发器)整个网站因开放交易而锁定。我可以在master..sysprocesses中看到阻止过程,从中我可以确定执行锁定的进程ID是我的网站的IIS进程,open_tran是1,并使用DBCC inputbuffer我可以看到查询,它只是一个通用的select ... from MyTable,它是Linq2Sql在加载对象时生成的SQL。无论如何,它肯定不是我写的任何SQL。

我已经扫描了我的整个代码库以查找任何旧的“BeginTransaction .. Commit / RollbackTransaction”块。纳达。一切都使用using (var ts = new TransactionScope()) { ... }块。我已经附加到IIS进程并在其他线程中的查询被阻止时捕获了Timeout异常。在事务块中没有其他可以找到的线程。与此同时,封锁过程根本不会消失。

有关如何进一步排查问题的任何想法?

1 个答案:

答案 0 :(得分:1)

首先确保调查显而易见的事情:当存在这样的事务时,将调试器附加到ASP进程并检查所有线程,确保在事务范围内没有线程阻塞/旋转。如果使用异步ADO.Net调用(.BeginExecuteReader)则更复杂,但考虑到使用LINQ,则不太可能使用异步数据库调用。

恕我直言,最可能的原因是死锁情况,其中死锁链在客户端进程内完成(在ASP.Net进程中)。这需要在客户端中进行某种形式的锁定(例如,C#的lock),并且当持有锁的线程进入数据库并阻塞时会发生这种情况,而另一个具有阻塞第一个线程的数据库锁的线程正在执行客户想要第一个线程保持。是一个死锁,但链循环涉及SQL Server中的alock和客户端进程中的另一个锁,因此SQL Server的死锁监视器无法检测到它。 very author of this site had run into this issue,所以它可能发生在任何人身上。好吧,任何在外部进行昂贵阻塞的人都会在进行数据调用时调用DB调用,这是多线程编程中不要做的第一条规则......我分歧了。即使您的应用程序中没有明确的锁定,也有一些具有此类锁定的组件,例如。 log4net的。如上所述,将调试器附加到流程将显示问题。

另一个可疑的嫌疑人可能是事务处理分布式事务。 TransactionScope有一个讨厌的习惯,当在单个事务范围内使用多个连接(例如,多个Linq数据上下文)时,提升到分布式事务,请参阅System.Transactions Integration with SQL Server (ADO.NET)。即使在客户端提交并断开连接之后,分布式事务依次具有持久的令人讨厌的属性。它必须由事务协调器确认,然后SQL Server才能实际提交它“for good”。在这种状态下,您可以检查sys.dm_tran_active_transactions,它会显示为dtc_state 2(PREPARED)。进一步调查,如果情况确实如此,将需要MSDTC故障排除。

最后,提醒一句:为什么只有简单的SELECT保持锁定?您使用的是harmful default constructor form new TransactionScope()吗?这将使用默认的SERIALIZABLE隔离级别,这是过度杀伤的99%,实际上是有害的。尝试使用显式TransactionOptions并将隔离级别设置为ReadCommitted