为什么在使用 Read Committed Snapshot Isolation 时需要 U 锁

Cle*_*ent 5 sql-server deadlock locking isolation-level snapshot-isolation

我认为自己是 Sql Server 锁定的初学者。

我的理解是,在使用 RCSI 时,Sql Server 不需要发出 S 锁,因为它使用行版本控制(在大多数情况下)。从http://technet.microsoft.com/en-us/library/jj856598(v=sql.110).aspx我们可以阅读以下关于 U 锁的信息:

用于可以更新的资​​源。防止在多个会话读取、锁定和稍后可能更新资源时发生的常见死锁形式。

知道了这一点,为什么Sql Server 需要发出U 锁(使用RCSI 时)?在我看来,Sql Server 可以简单地读取行,并在必须执行更新时直接请求 X 锁。

我一直在考虑这个的原因是因为我在更新同一个表的 2 个会话中遇到了死锁。像这样(为了清楚起见而简化):

第 1 节:

BEGIN TRAN
UPDATE t1 SET col1 = col1 + 100 WHERE col2 = value1
UPDATE t1 SET col1 = col1 + 100 WHERE col2 = value2
UPDATE t1 SET col1 = col1 + 100 WHERE col2 = value3
Run Code Online (Sandbox Code Playgroud)

第 2 节:

BEGIN TRAN
UPDATE t1 SET col1 = col1 + 100 WHERE indexedcol = value4
UPDATE t1 SET col1 = col1 + 100 WHERE indexedcol = value5
UPDATE t1 SET col1 = col1 + 100 WHERE indexedcol = value6
Run Code Online (Sandbox Code Playgroud)

从我在 Profiler 锁定报告中收集的信息来看,两个会话在表的某些行上都有一个 X 锁,并且都在彼此的 X 锁定行上请求 U 锁 => 死锁。但是,我知道每个并发会话总是会修改不同的行,因为值 X 在 2 个不同的并发会话中永远不会相同。也就是说,如果这些会话能够获得他们请求的 U 锁,sql server 就会意识到无论如何都没有必要升级 X 锁。

总之,我觉得,尽管 U 锁应该减少死锁,但它们实际上在这里造成了不必要的死锁。

我知道它们对于其他隔离级别可能很有价值,但对于 RCSI,我不明白......

Pau*_*ite 7

知道了这一点,为什么sql server需要发出U锁(使用RCSI时)?在我看来,sql server 可以简单地读取行,如果必须执行更新,则直接请求 X 锁。

与 SI 不同,RCSI 不检测更新冲突。正如Books Online 中所述,在 RCSI 下修改数据会读取当前提交的数据,而不是可能过时的版本。(在没有更新冲突检测的情况下,根据过期数据执行更新可能会导致“丢失更新”。)

获取更新锁是更新数据的非行版本控制查询的正常行为。它是针对常见的转换死锁原因的保护措施,但并不能保证在所有情况下都能避免死锁,尤其是在使用不同的访问路径(索引)来限定要更改的行的情况下。

您可以在我的 SQLperformance.com 文章“读取提交的快照隔离下的数据修改”中找到有关修改数据时 RCSI 确切行为的更多详细信息。在文章“Read Committed Snapshot Isolation”中一般有关于 RCSI 的进一步背景。

如果更新确实不相交,您可能会考虑使用快照隔离而不是 RCSI(这在这方面具有复杂的行为)来执行更改。