在正在运行的事务中更改事务隔离级别

Tom*_*rek 6 sql-server transaction isolation-level

我正在研究 SQL Server 中的事务隔离级别,并试图弄清楚在事务的生命周期中隔离级别发生变化时 SQL Server 的行为。

在 SQL Server 中似乎有这样的事情是可能的:

BEGIN TRANSACTION;

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

/* (some selects/inserts/updates/deletes) */

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

/* (some selects/inserts/updates/deletes) */

COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)

老实说,我想不出降低隔离级别有意义的例子,我只能想到一些场景,其中事务的一部分需要可序列化的隔离,而其他部分则不需要。我有一种感觉,将使用快照和行版本控制的隔离级别与其他类型的隔离级别混合使用效果不佳,但我找不到很多信息来支持这一点。

在实践中是否会发生单个事务在其生命周期内在多个隔离级别之间切换的情况?是否有一些注意事项和细节需要了解?

Tho*_*ers 4

您使用的语法是有效的,该SET TRANSACTION ISOLATION LEVEL命令之后执行的所有语句都将使用指定的隔离级别。

考虑下表:

CREATE TABLE dbo.test
(
    ID int identity(1,1) Primary key
    , Field nvarchar(10)
)
GO

INSERT INTO dbo.test(Field)
Values ('XXXXXXXXXX')
GO 100
Run Code Online (Sandbox Code Playgroud)

接下来,启动一个事务并执行 2 条语句。
第一个语句将使用REPEATABLE READ(当语句完成时不会释放其共享锁),第二个语句将使用SERIALIZABLE(它将获取并持有范围锁)

BEGIN TRANSACTION

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

SELECT *
FROM dbo.test 
WHERE id = 100

-- This will show a shared lock on key 100, the lock is kept during the transaction because repeatable read is used
SELECT *
FROM sys.dm_tran_locks 
WHERE request_session_id = @@SPID

-- Isolation level 3 = repeatable read
SELECT transaction_isolation_level 
FROM sys.dm_exec_sessions 
WHERE session_id = @@SPID

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

SELECT *
FROM dbo.test
WHERE id >= 0 and id < 10

-- This will show the previous shared lock on key 100 + additional RangeS-S key locks taken by the serializable statement
SELECT *
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID

-- Isolation level 4 = Serializable
SELECT transaction_isolation_level 
FROM sys.dm_exec_sessions 
WHERE session_id = @@SPID

COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)

sys.dm_tran_locks查看and的输出,sys.dm_exec_sessions您可以看到这两个语句在同一事务中发出不同的锁并使用不同的隔离级别。尽管这在技术上是可行的,但我倾向于在事务中使用单一隔离级别。

SNAPSHOT将隔离与悲观锁定结合起来时,您可能会得到一些意想不到的结果。

想象一下单独启动一个事务SNAPSHOT并读取一些数据。
接下来,第二个会话会更新您刚刚读取的数据。
第一个事务切换READ COMMITTED并再次读取数据,SQL Server 现在将返回更新后的值。
最后,您再次切换到SNAPSHOT隔离并读取数据。您将使用之前创建的快照进行读取,并且将返回旧值。

-- Note: you have to set your isolation level to snapshot before starting your transaction otherwise you will get an error

SET TRANSACTION ISOLATION LEVEL SNAPSHOT 

BEGIN TRANSACTION 

-- Returns XXXXXXXXXX
SELECT * 
FROM dbo.test 
WHERE id = 1

-- Run "UPDATE dbo.test SET Field = 'YYYYYYYYYY' WHERE id = 1" in a separate session

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

-- Returns YYYYYYYYYY
SELECT * 
FROM dbo.test
WHERE id = 1

SET TRANSACTION ISOLATION LEVEL SNAPSHOT 

-- Returns XXXXXXXXXX
SELECT * 
FROM dbo.test 
WHERE id = 1

COMMIT
Run Code Online (Sandbox Code Playgroud)