可重复读(Repeatable Read) - 我理解得对吗?

12

试图完全理解SQL Server的隔离级别-尤其是REPEATABLE READ。

我有一个存储过程,它启动一个事务并在一些数据周围放置一个光标(嘘声)。这可以是相当大块的数据,所以可能需要一段时间才能完成。

然后它将提交或回滚。

在此期间,在事务关闭之前,如果有人调用导致读取其中一些受影响行的方法,我的理解是该方法将停滞,直到第一个方法完成。然后他们将获得数据(只要没有超时发生)

我认为我是对的,但问题是-我是吗?


实际上,这里的答案与以下两点有很大关系:1)您正在使用哪种CURSOR选项;2)SQL Server的版本是多少,因为2005及更高版本在幕后尝试使用快照,即使您没有要求也是如此。 - RBarryYoung
2个回答

25

REPEATABLE READ 会防止 SELECT 语句在事务结束之前解除共享锁。

使用事务 1 的隔离级别为 READ COMMITTED,你可以在事务 1 中选择一行并在事务 2 中更新它。

使用事务 1 的隔离级别为 REPEATABLE READ,你不能在事务 2 中更新在事务 1 中选择的同一行。

场景:

READ COMMITTED

1 SELECT -- places a shared lock and immediately lifts it.
2 UPDATE -- places an exclusive lock. Succeeds.
1 SELECT -- tries to place a shared lock but it conflicts with the exclusive lock placed by 2. Locks.

可重复读

1 SELECT -- places a shared lock and keeps it
2 UPDATE -- tries to places an exclusive lock but it's not compatible with the shared lock. Locks
1 SELECT -- the lock is already placed. Succeeds.

更新:

关于你的问题:在SQL Server中,即使使用 REPEATABLE READSELECT语句也不会相互锁定,因为它们放置的共享锁是兼容的。

CREATE TABLE t_lock (id INT NOT NULL PRIMARY KEY, value INT NOT NULL)
INSERT
INTO    t_lock
VALUES (1, 1)

-- Session 1

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION
DECLARE @id INT
DECLARE cr_lock CURSOR DYNAMIC
FOR
SELECT  id
FROM    t_lock
OPEN    cr_lock
FETCH   cr_lock

id
--
1

-- Session 2

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION
DECLARE @id INT
DECLARE cr_lock CURSOR DYNAMIC
FOR
SELECT  id
FROM    t_lock
OPEN    cr_lock
FETCH   cr_lock

id
--
1

-- Session 1

DEALLOCATE cr_lock
COMMIT

-- Session 2

DEALLOCATE cr_lock
COMMIT

2
@Duncan - 不是的,你需要使用 READ UNCOMMITTED(或者在 SELECT 中应用 WITH (NOLOCK) 提示) - AdaTheDev
好的,那么如果我有以下情况怎么办(同时调用2个单独的方法):1-可重复读取事务-SELECT&UPDATE 2-读取已提交的事务-尝试选择 1-交易提交会发生什么? 2会立即获得“脏”数据,还是会阻止直到“1”完成? - Duncan
1
@Duncan:第一次事务将使用SELECT放置一个S锁,并将其升级为U,然后再使用UPDATE升级为X。第二个事务将尝试放置一个S锁,但会失败并被锁定。第一个事务提交,第二个事务接收到锁并看到更新的记录。 - Quassnoi
太棒了,这正是我之前测试所显示的,我会再试一次 - 这让我对理论的正确性更有信心! - Duncan

3

正确。

来自MSDN的完整描述:

指定语句不能读取其他事务已修改但尚未提交的数据,也不能修改当前事务已读取的数据,直到当前事务完成。

在事务中由每个语句读取的所有数据上放置共享锁,并且这些锁将一直保持到事务完成。这可以防止其他事务修改当前事务已读取的任何行。其他事务可以插入与当前事务发出的语句匹配的新行。如果当前事务重试该语句,则会检索到新行,从而导致幻读。由于共享锁保持到事务结束而不是在每个语句结束时释放,因此并发性低于默认的READ COMMITTED隔离级别。仅在必要时使用此选项。


描述很好,但是对于问题的实际答案(两个SELECTs,没有UPDATEs)是“不正确”的 :), 两个选择都不会相互锁定,无论是使用RC还是RR - Quassnoi

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