更新锁的用途

joj*_*ojo 4 sql-server deadlock locking

我正在阅读了解 SQL Server 中的锁定。但不太明白更新锁的用途。

详细说明如下:

更新锁

更新 (U) 锁可防止常见形式的死锁。典型的更新模式包括事务读取记录、获取资源(页或行)上的共享 (S) 锁,然后修改该行,这需要将锁转换为排他 (X) 锁。如果两个事务获取资源上的共享模式锁,然后尝试同时更新数据,则一个事务会尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待,因为一个事务的排它锁与另一个事务的共享模式锁不兼容;发生锁等待。第二个事务尝试为其更新获取独占 (X) 锁。由于两个事务都转换为排它 (X) 锁,并且它们都在等待另一个事务释放其共享模式锁,因此会发生死锁。

为了避免这种潜在的死锁问题,使用更新(U)锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁将转换为排他 (X) 锁。否则,锁将转换为共享模式锁。

考虑下面两个事务(两个事务都执行Isolation Level Repeatable Read以在事务期间保持 S 锁):

在 TRAN1 中执行以下 SQL。

BEGIN TRAN
SELECT BrandName FROM dbo.Brand WHERE BrandId=2
Run Code Online (Sandbox Code Playgroud)

现在,TRAN1 为 RID 授予 S 锁

在 TRAN2 中执行以下 SQL

 BEGIN TRAN
 SELECT BrandName FROM dbo.Brand WHERE BrandId=2
Run Code Online (Sandbox Code Playgroud)

现在,TRAN2 为与 TRAN1 相同的 RID 资源授予 S 锁

在 TRAN1 中执行以下 SQL

UPDATE dbo.Brand SET BrandName='YBrand' WHERE BrandId=2
Run Code Online (Sandbox Code Playgroud)

现在,TRAN1 S锁转换为U锁,U锁等待TRAN2 S锁释放转换为X锁

在 TRAN2 中执行以下 SQL

UPDATE dbo.Brand SET BrandName='ZBrand' WHERE BrandId=2
Run Code Online (Sandbox Code Playgroud)

然后就会出现死锁。

上行死锁正如描述的那样,是用U锁来防止的。但僵局仍然发生。

所以我的问题是:U锁和X锁有什么不同?哪些情况下可以不用X锁来防止死锁呢?

mar*_*c_s 5

该UPDATE操作分为两步:

  1. (U)首先使用(更新)锁读取现有值

  2. 然后该锁被转换为独占(X)锁以写回新的(更新的)值。

由于您的REPEATABLE READ隔离级别,并且因为您这样安排语句,是的,您将陷入僵局。但我真的不明白这与锁有什么关系update......(这实际上只是因为您这样安排代码并且因为您正在使用REPEATABLE READ)。

锁的主要“好处”(U)是(S)此时其他共享锁仍然是可能的。例如,当一个事务读取要使用锁更新的值时,另一个事务可以使用a 中的共享锁(U)读取相同的值(如果您有独占锁,则这不起作用,例如当您执行 a 时)(S)SELECT(X)DELETE

如果您有两个事务都只单独执行UPDATE(不SELECT使用REPEATABLE READ) - 那么(U)第一个事务所采取的锁定将阻止第二个事务也读取该值(因为(U)锁不兼容 - 如果TRAN1在一行上有更新锁,TRAN2无法获取其更新锁)。这使得“读取现有值、更新它、写回它”成为原子操作,并防止两个事务同时在同一行上启动更新过程。