当前位置: 首页 > 知识库问答 >
问题:

更新锁的用途

孔斌
2023-03-14

我正在读《理解SQL Server中的锁定》。但我不太理解更新锁的目的。

详细说明如下:

更新锁

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

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

考虑以下两个事务(两个事务都在隔离级别可重复读取执行,以便在事务期间保持 S 锁):

在 TRAN1 中执行以下 SQL。

BEGIN TRAN
SELECT BrandName FROM dbo.Brand WHERE BrandId=2

现在,TRAN1 为 RID 授予 S 锁

在TRAN2中的SQL下执行

 BEGIN TRAN
 SELECT BrandName FROM dbo.Brand WHERE BrandId=2

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

在TRAN1中执行以下SQL

UPDATE dbo.Brand SET BrandName='YBrand' WHERE BrandId=2

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

在TRAN2中的SQL下执行

UPDATE dbo.Brand SET BrandName='ZBrand' WHERE BrandId=2

然后出现死锁。

Up死锁与U锁用于防止死锁的描述完全相同。但僵局仍然存在。

所以我的问题是:U锁和X锁有什么不同?哪种情况使它能够防止死锁而不是使用X锁?

共有2个答案

翟承志
2023-03-14

更新 (U) 锁会自动放置在 DB on UPDATE 语句中的数据上。主要任务是保护数据库中的数据免受多个事务的同时更改,并避免死锁。

更新语句由三部分组成:读取数据、计算新值、写入数据。我们无法为读取部分应用独占(X)锁。因此,更新锁实际上不是一种单独的锁,而是共享锁和独占锁的混合。

假设两个进程都在搜索要修改的相同资源(例如,客户表中的同一个客户行),使用不同的访问路径,并且它们都可以同时到达所需的资源。如果它们都在检查的数据上获取SHARED锁,它们都可以锁定它们想要更改的资源,但是在进行修改之前,它们必须将它们的锁转换为EXCLUSve锁。由于另一个进程将具有SHARED锁,因此无法授予EXCLUSve锁。每个进程都有一个共享锁,每个进程都试图将其更改为独家锁,但由于另一个进程的存在,两个进程都无法继续。这是一种死锁情况,称为“转换死锁”。

如果SQL Server使用更新锁,就不会发生死锁。

更新锁与共享锁兼容,但与独占锁或其他更新锁不兼容。因此,如果两个进程搜索同一个数据资源,第一个到达的进程将获得一个更新锁,然后第二个进程不能获得任何锁,将等待第一个进程完成。由于第一个进程没有被阻塞,它可以将其更新锁转换为独占锁,进行数据修改,并完成其事务和释放其锁。然后第二个进程可以做出改变。

共享(S)锁-当需要读取对象时发生。

独占(X)锁定-用于阻止其他事务修改或访问锁定对象。

傅元龙
2023-03-14

UPDATE操作是一个两步过程:

>

  • 首先用 (U)(更新)锁读取现有值

    然后该锁被转换成排他的< code>(X)锁,以写回新的(更新的)值。

    由于您的<code>REPEATABLE READ</code>隔离级别,以及您这样安排语句,是的,您将陷入死锁。但我真的不明白这与<code>update</code>锁有什么关系…(这实际上只是因为您这样安排了代码,并且您使用的是<code>REPEATABLE READ</code>)。

    (U)锁的主要“好处”是,其他(S)共享锁在那时仍然是可能的。E、 g.当一个事务读取要用(U)锁更新的值时,另一个事务可以在SELECT中用(S)共享锁读取相同的值(如果您有一个互斥 锁,例如当您执行 时,这不起作用)

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

  •  类似资料:
    • 这是我的伪代码: 我想问,如果条件等于“key”的行已经被删除,那么“select for update”阻止的锁是否可以自动解锁,这意味着如果另一个进程在此点进入并选择相同的“key”,它就不能被此进程阻止?

    • 我在更新和获取键/值时使用ServiceStack Redis的AcquireLock方法,如下所示: 我扩展了AcqurieLock方法,以接受锁密钥过期时的额外参数。所以我想知道我是否需要AcquireLock?我的类在每个操作中都使用AcquireLock,比如Get 但是这种方法并不是每次都有效。例如,如果锁中的操作抛出异常,那么密钥将保持锁定状态。对于这种情况,我已将DefaultLoc

    • 我想获取一个使用。与相比,仅锁定指定表中的行,并且连接的行不会被阻塞。但是,当我尝试使用以下代码段使用ecto执行此操作时,它失败了。 原因是ecto为使用别名,例如,这意味着我还必须在lock表达式中使用别名才能使其正常工作。 使用别名看起来不是一种正确的方法。还有其他方法可以让它工作吗?

    • 我从过去几个月开始使用spring,我有一个关于交易的问题。我在我的spring批处理作业中有一个java方法,它首先执行一个select操作以获取前100行,状态为“未完成”,并对所选行进行更新以将状态更改为“进行中”。由于我正在处理大约1000万条记录,我想运行批处理作业的多个实例,每个实例都有多个线程。对于单个实例,为了确保两个线程不会获取同一组记录,我将方法设置为同步。但是,如果我运行批处

    • 我在更新和插入查询之间的应用程序中遇到了死锁,我无法理解为什么以导致死锁的方式给出锁。 null 插入- null

    • 我在使用PostgreSQL 9.3。我是唯一一个处理数据库的人,我的代码按顺序运行单元测试的查询。 大多数情况下,以下更新查询运行正常,但有时会锁定PostgreSQL server。然后,查询似乎永远不会结束,而通常只需要3秒钟。我必须确定查询在单元测试上下文中运行,即数据完全相同,而锁是否发生。代码是唯一更新数据的进程。 我知道在对自更新表使用更新查询时,PostgreSQL可能存在锁问题。