我正在做一些测试,以尝试了解快照隔离的工作原理……但我没有。我的数据库中有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)
因此,这次它按我的预期工作,并且没有考虑最新的插入内容。
如何解释这种行为?第一次访问每个表后,隔离是否开始起作用?
答案 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)
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
不是Commit
或Rollback
。
之后,它将从Table中读取数据。
在@ s2中,Begin Tran
没有回滚或Commit
。
尽管所有记录都在@ s1中提交,
它不获取3。它获取在同一表上发布T Sql之前的committed
的1,2。
如果在@ S2中完成了回滚或提交操作,则输出将为(1,2,3)。 由于@ s1中的所有Insert语句都已提交。
在Transaction
为Commit
或Rollback
之后,它将从中读取数据。
在另一个示例中,
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
读取。
在Commit
或Rollback
之后,它将再次刷新TempDB
的记录。
所以@ s2中的输出将为1