隔离级别和select for update

5

关系型数据库的隔离级别和select for update之间有什么联系?

如果我使用普通的JDBC连接SQL Server,并将隔离级别设置为READ_REPEATABLE并使用简单的select,我是否会看到可重复读中的不一致性?或者我应该始终使用select for update来避免事务中的不一致的可重复读取?如果是这样,隔离级别怎么运作呢?

3个回答

2

SQL Server没有select ... for update语法。在SQL Server中,相当于使用UPDLOCK表提示。

在原子事务中读取一行并立即更新同一行时,使用此提示。例如:

最初的回答

declare @balance = (select balance from account where accountId = @id)
update account set balance = @balance + @amount where accountId = @id

在READ COMMITTED隔离级别下,或者在没有多语句事务的任何隔离级别下,多个会话可以运行第一个查询,并在更新余额时丢失更新。

使用REPEATABLE READ或SERIALIZABLE隔离级别将防止此更新异常,但是如果有任何并发事务读取该行,则它们将通过阻止第一个写入器来实现,如果其中一个其他事务尝试更新该行,则会导致死锁。

大多数情况下,这种行为不值得处理死锁的性能成本和烦恼。因此,您可以使用“select for update”(也称为UPDLOCK)在读取时对行进行U锁定,并阻止后续读取器获取冲突锁。

例如:

declare @balance = (select balance from account with (updlock) where accountId = @id)
update account set balance = @balance + @amount where accountId = @id

嗨,David,那么我应该同时采用UPDLOCK并将隔离级别设置为REPEATABLE READ或SERIALIZABLE来实现并发吗?或者在REPEATABLE READ或SERIALIZABLE中,简单的SELECT语句也会获取锁吗? - OMER HAYAT
对于“select for update”的场景,您应该在默认的READ COMMITTED隔离级别下使用事务,并使用UPDLOCK。 - David Browne - Microsoft

1

不太确定您所说的“不一致的可重复读取”,但在SQL Server中使用REPEATABLE READ隔离级别,共享锁将保持到事务结束。只有提交的数据将被返回,并且其他会话在事务提交之前(或在没有显式事务的情况下自动提交)不能修改读取的行。

REPEATABLE READ无法防止其他会话插入新行,因此在同一事务中重新运行相同的查询可能会返回未最初返回的新行(幻读)。

文档更详细地描述了这一点以及其他事务隔离级别。


嗨,丹,当在REPEATABLE READ中执行事务时,我是否需要显式获取更新锁(使用“select for update”即UPDLOCK),或者简单的选择会在幕后获取共享锁? - OMER HAYAT
@OMERHAYAT,不需要显式锁提示,因为隔离级别声明会自动处理所需的共享锁。 - Dan Guzman
那么我们可以安全地说,在READ_COMMITTED状态下显式获取锁(使用“select for update”即UPDLOCK)与在READ_REPEATABLE隔离级别下使用简单选择一样一致吗? - OMER HAYAT
使用UPDLOCK提示和READ COMMITED级别,读取将类似于REPEATABLE READ,除了带有提示的更新锁将阻止其他选择查询。要获得与REPEATABLE READ相同的行为,可以指定HOLDLOCK提示。但是,当声明的会话隔离级别适合您的需求时,没有理由使用提示。 - Dan Guzman

0

没有必要使用提示或其他方式干预默认行为。 SQL Server根据您声明的所需级别保证事务隔离级别。 REPEATABLE READ保证在事务内,您的语句将看到已读取数据的一致视图,但不能保证不会出现幻象行。为了避免后者,您需要使用SERIALIZABLE。 隔离级别越高,当然并发惩罚就越高。

请参阅JDBC页面的隔离级别SET TRANSACTION ISOLATION LEVEL通用页面以获得更深入的讨论。

希望对您有所帮助


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