私は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;
正直に言うと、分離レベルの低下が理にかなっている例を考えることはできません。トランザクションの一部がシリアル化可能な分離を必要とし、他の部分は必要としないいくつかのシナリオしか考えられません。スナップショットを使用した分離レベルと行のバージョン管理を他の種類の分離レベルと混在させることはうまくいかないように感じますが、これをバックアップするための多くの情報が見つかりません。
単一のトランザクションがその存続期間中に複数の分離レベルを切り替えるのは、実際に起こっていることですか?知っておくべき注意点と詳細はありますか?
使用している構文は有効です。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
次に、トランザクションを開始し、2つのステートメントを実行します。
最初のステートメントはREPEATABLE READ
を使用し(ステートメントの完了時に共有ロックを解放しません)、2番目のステートメントは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
sys.dm_tran_locks
およびsys.dm_exec_sessions
の出力を見ると、2つのステートメントが異なるロックを発行し、同じトランザクション内で異なる分離レベルを使用していることがわかります。技術的には可能ですが、私はトランザクション内で単一の分離レベルを使用する傾向があります。
SNAPSHOT
分離を悲観的ロックと組み合わせると、予期しない結果が生じる可能性があります。
SNAPSHOT
分離でトランザクションを開始し、いくつかのデータを読み取ることを想像してください。
次に、2番目のセッションで、読み取ったばかりのデータを更新します。
最初のトランザクションが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