0

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

时不时地(尚未确定触发器)整个网站由于未完成的交易而锁定。中可以看到阻塞进程master..sysprocesses,从中可以识别出做加锁的进程ID是我网站的IIS进程,open_tran为1,使用DBCC inputbuffer可以看到查询,就是泛型select ... from MyTable,就是SQL加载对象时由 Linq2Sql 生成。无论如何,这肯定不是我写的任何 SQL。

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

有关如何进一步解决此问题的任何想法?

4

1 回答 1

1

首先确保您调查了明显的情况:当存在此类事务时,将调试器附加到您的 ASP 进程并检查所有线程,确保在事务范围内没有线程阻塞/旋转。如果您使用异步 ADO.Net 调用 ( .BeginExecuteReader) 会更复杂,但鉴于您使用 LINQ,您不太可能使用异步 DB 调用。

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

另一个可能的嫌疑人可能是将事务注册到分布式事务中。TransactionScope当在单个事务范围内使用多个连接(例如,多个 Linq 数据上下文)时,这种讨厌的习惯会提升为分布式事务,请参阅System.Transactions Integration with SQL Server (ADO.NET)。分布式事务反过来具有这种令人讨厌的特性,即使在客户端提交和断开连接之后仍然存在。它必须由事务协调器确认,然后 SQL Server 才能真正“永久”提交它。在这种状态下,您可以进行检查sys.dm_tran_active_transactions,它将显示为dtc_state2 ( PREPARED)。进一步调查,如果事实证明是这种情况,则需要 MSDTC 故障排除。

最后,提醒一句:为什么简单的 SELECT 会持有锁?您是否使用有害的默认构造函数形式new TransactionScope()?这将使用默认的 SERIALIZABLE 隔离级别,这是 99% 的过度杀伤并且实际上是有害的。尝试使用显式TransactionOptions并将隔离级别设置为ReadCommitted.

于 2012-07-12T15:09:49.840 回答