关系型数据库的隔离级别和select for update
之间有什么联系?
如果我使用普通的JDBC连接SQL Server,并将隔离级别设置为READ_REPEATABLE并使用简单的select
,我是否会看到可重复读中的不一致性?或者我应该始终使用select for update
来避免事务中的不一致的可重复读取?如果是这样,隔离级别怎么运作呢?
关系型数据库的隔离级别和select for update
之间有什么联系?
如果我使用普通的JDBC连接SQL Server,并将隔离级别设置为READ_REPEATABLE并使用简单的select
,我是否会看到可重复读中的不一致性?或者我应该始终使用select for update
来避免事务中的不一致的可重复读取?如果是这样,隔离级别怎么运作呢?
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
不太确定您所说的“不一致的可重复读取”,但在SQL Server中使用REPEATABLE READ
隔离级别,共享锁将保持到事务结束。只有提交的数据将被返回,并且其他会话在事务提交之前(或在没有显式事务的情况下自动提交)不能修改读取的行。
REPEATABLE READ
无法防止其他会话插入新行,因此在同一事务中重新运行相同的查询可能会返回未最初返回的新行(幻读)。
文档更详细地描述了这一点以及其他事务隔离级别。
UPDLOCK
提示和READ COMMITED
级别,读取将类似于REPEATABLE READ
,除了带有提示的更新锁将阻止其他选择查询。要获得与REPEATABLE READ
相同的行为,可以指定HOLDLOCK
提示。但是,当声明的会话隔离级别适合您的需求时,没有理由使用提示。 - Dan Guzman没有必要使用提示或其他方式干预默认行为。 SQL Server根据您声明的所需级别保证事务隔离级别。 REPEATABLE READ保证在事务内,您的语句将看到已读取数据的一致视图,但不能保证不会出现幻象行。为了避免后者,您需要使用SERIALIZABLE。 隔离级别越高,当然并发惩罚就越高。
请参阅JDBC页面的隔离级别和SET TRANSACTION ISOLATION LEVEL通用页面以获得更深入的讨论。
希望对您有所帮助