4

我正在做一些测试以尝试了解快照隔离的工作原理……但我没有。我在我的数据库中(对atmSET ALLOW_SNAPSHOT_ISOLATION ON不感兴趣)。READ_COMMITTED_SNAPSHOT然后我做以下测试。我将通过 [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)

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

insert _g1 select 3

@[s2]:

select * from _g1
(Output: 1,2)

所以,这一次它按我的预期工作,并没有考虑最新的插入。

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

4

2 回答 2

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 语句之后“开始计数”,而不是在“开始事务”语句之后立即开始计数。

于 2019-11-21T10:28:59.297 回答
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。或者在 T-Sql 中提到的任何表格Transaction

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

它将从TempDB直到Transaction不是Commit或的数据中读取数据Rollback。之后,它将从 Table 中读取数据。

在@s2 中,Begin Tran没有 RollBack 或Commit.

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

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

TransactionisCommit或之后Rollback,它将从 中读取数据。

在另一个例子中,

Truncate table _g1.

我们首先开始@s2

Set Transaction Isolation Level Snapshot
Begin Tran
select * from _g1

输出:没有记录。

这里数据库引擎维护了自己的 table 版本_g1。由于没有记录_g1,所以TempDB是空的。

在@s1 中,

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

在@s2 中,

如果你只是简单地运行

select * from _g1

或者你运行所有脚本

输出仍然没有。因为我们还没有提交,rollback所以它继续从TempDB.

Commit或之后Rollback,会再次刷新记录TempDB

所以@s2 中的输出将是 1

于 2019-11-25T08:28:07.937 回答