快照隔离行为。第一次查询时“触发”?

时间:2019-11-18 08:04:34

标签: sql sql-server tsql isolation-level snapshot-isolation

我正在做一些测试,以尝试了解快照隔离的工作原理……但我没有。我的数据库中有SET ALLOW_SNAPSHOT_ISOLATION ON(对READ_COMMITTED_SNAPSHOT atm不感兴趣)。然后,我进行以下测试。我将通过[s1]和[s2]标记来标记不同的会话(在我的ssms中实际上是不同的选项卡),[s2]是孤立的会话,并且[s1]模拟另一个非孤立的会话。

首先,创建一个表,然后给它一行。 @ [s1]:

create table _g1 (v int)
insert _g1 select 1
select * from _g1
(Output: 1)

现在让我们开始一个孤立的事务。 @ [s2]:

set transaction isolation level snapshot
begin tran

插入另一行@ [s1]:

insert _g1 select 2

现在,让我们看看孤立的事务“看到”了什么,@ [s2]:

select * from _g1
(Output: 1,2)

奇怪。隔离不应该从“开始转换”那一刻开始“计数”吗?在这里,它不应该返回2 ....让我们再做一次。 @ [s1]:

insert _g1 select 3

@ [s2]:

select * from _g1
(Output: 1,2)

因此,这次它按我的预期工作,并且没有考虑最新的插入内容。

如何解释这种行为?第一次访问每个表后,隔离是否开始起作用?

2 个答案:

答案 0 :(得分:4)

快照隔离适用于行版本控制。对于一行中的每次修改,数据库引擎都会维护该行的先前版本和当前版本,以及进行该修改的事务的序列号(XSN)。

在[s2]中对事务使用快照隔离时:

  

数据库引擎读取事务中的一行并检索   tempdb中序列号最接近的行版本,以及   低于交易序号。

(请参阅https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/snapshot-isolation-in-sql-server中的“快照隔离和行版本控制的工作原理”)。直到发出DML语句,才分配[s2]中事务的事务序列号XSN2。

sys.dm_tran_active_snapshot_database_transactions是一个DMV,它返回一个虚拟表  用于生成或潜在访问行版本的所有活动事务。您可以查询此视图以获取有关访问行版本的活动事务的信息。

要验证以上所有内容,您可以尝试:

@ [s1]

create table _g1 (v int)

@ [s2]

set transaction isolation level snapshot
begin tran

select * from sys.dm_tran_active_snapshot_database_transactions  -- < No XSN has been assigned, yet. Zero rows are returned.

select * from _g1 --< XSN2 is now assigned.
(Output: zero rows)

select * from sys.dm_tran_active_snapshot_database_transactions  -- < XSN2 has been assigned and the corresponding record is returned.

@ [s1]

insert _g1 select 1
select * from _g1
(Output: 1)

@ [s2]

select * from _g1
(Output: zero rows)

请参阅https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-tran-active-snapshot-database-transactions-transact-sql?view=sql-server-ver15中有关发布XSN的备注:

  

sys.dm_tran_active_snapshot_database_transactions报告分配了事务序号(XSN)的事务。当事务首次访问版本存储时,将分配XSN。在启用了快照隔离或使用行版本控制读取提交隔离的数据库中,示例显示了何时将XSN分配给事务:

     
      
  • 如果事务在可序列化的隔离级别下运行,则在该事务首次执行导致创建行版本的语句(例如UPDATE操作)时会分配XSN。

  •   
  • 如果事务在快照隔离下运行,则在执行任何数据操作语言(DML)语句(包括SELECT操作)时都会分配XSN。

  •   

因此,为回答您的问题,快照隔离在事务中发出的第一个“ SELECT”或其他DML语句之后而不是在“ begin trasaction”语句之后立即“开始计数”。

答案 1 :(得分:2)

您可以Set Transaction Isolation Level Snapshot 在数据库级别或会话级别。

在我们的示例中,我们设置为Session level。 因此Isolation Level Snapshot仅在声明它的那个会话中起作用。 其次,您必须发出T-Sql语句。

在@ s2中,

Set Transaction Isolation Level Snapshot
Begin Tran

这里Transaction是打开的,但是没有T-Sql

那么Snapshot Version将维护哪个表?

Set Transaction Isolation Level Snapshot
Begin Tran
select * from _g1

这里isolation level将在表_g1上工作。或在Transaction中的T-Sql中提到过的表。

换句话说,它将为T-Sql的TempDB中提到的TRANSACTION中的所有表维护记录的自己版本。

它将从TempDB读取数据,直到Transaction不是CommitRollback。 之后,它将从Table中读取数据。

在@ s2中,Begin Tran没有回滚或Commit

尽管所有记录都在@ s1中提交, 它不获取3。它获取在同一表上发布T Sql之前的committed的1,2。

如果在@ S2中完成了回滚或提交操作,则输出将为(1,2,3)。 由于@ s1中的所有Insert语句都已提交。

TransactionCommitRollback之后,它将从中读取数据。

在另一个示例中,

Truncate table _g1

我们首先开始@s2

Set Transaction Isolation Level Snapshot
Begin Tran
select * from _g1

输出:无记录。

此处数据库引擎为表_g1维护了自己的版本。 由于_g1中没有记录,因此TempDB为空。

在@ s1中,

insert _g1 select 1
select * from _g1
(Output: 1)

在@ s2中,

如果您只运行

select * from _g1

或者您运行所有脚本

输出仍然为空。因为我们尚未提交或rollback,所以它继续从TempDB读取。

CommitRollback之后,它将再次刷新TempDB的记录。

所以@ s2中的输出将为1