一个使用“读取已提交”隔离级别的事务在Sql Server中是否可能导致死锁?

12
我对“死锁”的理解是-两个进程试图争夺同一资源-通常是两个进程试图“写入”相同的数据行。
如果一个进程只是读取数据,而另一个进程正在更新数据,那么这算是资源争用吗?然而,在我们的数据库中,设置为默认事务级别“ReadCommitted”,我们看到了几个死锁异常。
“ReadCommitted”定义-已修改的数据(但尚未提交)不能被读取。这是可以接受的-但是如果SQL Server遇到正在进行的“脏读取”,它应该抛出死锁异常吗?
有人在这种情况下有实际经验吗?我找到了一篇博客文章(由stackoverflow开发者撰写),声称这可能是真实的。
2个回答

11

ReadCommitted 事务隔离级别在读取行时最初获取资源上的共享锁,但在尝试更新该行时会获得资源上的排他锁。多个用户可以在同一行上拥有共享锁并且不会产生影响,但是如果一个用户尝试更新行,则会在该行上获得独占锁,这可能会导致死锁。假设有 User1 和 User2 两个用户,它们都拥有共享锁并尝试更新某些记录,它们都会在其他用户提交事务所需的行上获得排他锁,这将导致死锁。
如果出现死锁并且没有设置优先级别,则 SQL Server 会等待一段时间,然后回滚更加廉价的事务。
编辑
是的,如果 User1 只是读取数据而 User2 尝试更新一些数据,并且该表上有非聚集索引,则可能会发生以下情况:

  1. User1 读取一些数据并获取非聚集索引上的共享锁以执行查找,然后尝试获取包含数据的页上的共享锁以返回数据本身。

  2. User2 首先获取包含数据的数据库页上的独占锁,然后尝试在索引上获取排他锁以更新索引。


你也在谈论user1和user2都试图更新同一条记录的情况。在这种情况下死锁是可以接受的。但是,我想知道是否有人看到过两个事务之间产生死锁的结果 - 其中一个是写操作,另一个是读操作(当然是在相同的数据上)。 - user2736158
1
请看一下,我已经更新了答案并进行了解释。 - M.Ali
@M.Ali 哪种隔离级别可以解决这个问题? - Berecz Balázs

5
是的,这种情况是可能发生的。想象一下,您有两个进程,每个进程都有自己的事务。第一个进程更新TableA,然后尝试更新TableB。第二个进程更新TableB,然后尝试更新TableA。如果你运气不好,两个进程都完成了第一步,然后无限期地等待另一个进程完成第二步。
顺便说一下,这是避免死锁最常见的方法之一:在更新表的顺序上保持一致。如果两个进程首先更新TableA,然后再更新TableB,就不会发生死锁。

好的 - 你正在描述两个更新事务。在这种情况下,遇到死锁是有道理的。我的问题是 - 其中一个事务只是一个读取操作 - 没有更新。另一个事务正在写入正在被读取的相同记录。这怎么可能导致死锁呢? - user2736158
从我的角度来看,这不应该死锁,但是如果您编辑问题并提供更多交易尝试执行的详细信息,我们可能会找到问题的根本原因。 - acfrancis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接