我们从一个例子开始来了解版本存储区。本例中数据库启用RCSI(使用行">版本控制的已提交读隔离级别)和SI(快照隔离级别)。
当数据库启用了RCSI或者SI时,每当行有更新时
就会创建一个行版本。此行版本在版本存储器保存到不需要为止。
那么SQL Server什么时候不再保存这个行版本呢?您可能联想到一些相关的问题,比如,如果不回收(删除)行版本会有什么影响?一次回收一个还是一组行版本?每
个用户表
都有不同的结构,那么
SQL Server是不是分别存储每个表的行版本的?行版本控制的开销大不大?
首先,我们创建一个数据库并开启SI/RCSIcreatedatabasesnapshottestgo -- Setting databaseforsnapshot based isolations. AS you can-- see that enabling SI/RCSI is a simple DDL operation.alterdatabase snapshottest setread_committed_snapshotONgo alterdatabase snapshottest setallow_snapshot_isolationONgo -- check the snapshot state of the database and it will-- show that both SI and RCSI have been enabled.select is_read_committed_snapshot_on, snapshot_isolation_state_desc, snapshot_isolation_state fromsys.databaseswhere name='snapshottest'go --create a table with
each row > 1000 bytescreatetable t_version(c1 int, c2 char(1000))go --Load 50 rows. Note,I start the transaction but did not--commit it yet.Begin trandeclare @i intselect @i = 0while (@i < 50)begininsertinto t_version values (@i,'hello')set @i = @i + 1end 接下来可以通过DMV查看版本存储区,你会发现显示版本存储区行数为0,这是我们实验的第一个结论,
插入(Insert)命令不会产生行版本,因为根本没有之前的版本可言(有一个特例使插入产生行版本,我们将稍后讨论)。这同时意味着批量导入也不会产生行版本。selectCOUNT(*)fromsys.dm_tran_version_store-- Now commit the transactioncommit 接下来让我们更新表格中的前50行。因为行数据被更新了,SQL Server将复制旧版本进入版本存储区。
所以版本存储区中将有50行行版本。-- update all the rows in the tableupdate t_version set c2 ='test10' -- the following statement returns the count of versioned row. -- And, for the case here, it will return 50selectCOUNT(*)fromsys.dm_tran_version_store 请注意,我们没有使用显式事务,因此这个Update语句是一个隐式事务,并且是这个隐式事务中唯一的语句。同时没有并发的事务需要使用行版本,所以这些行版本会被SQL Server当做垃圾数据处理。如果过几分钟在运行下面语句,就能发现行版本被回收处理了。 -- the following statement returns 0 if run after a minute or so selectCOUNT(*)fromsys.dm_tran_version_store SQL Server根据事务状态判断版本是否不再需要,从而决定垃圾处理该版本。在最差的情况下,如果有一个长事务正在运行,而且这个长事务要么创建了行版本要么需要用到它,那么这个行版本就不能被删除,版本存储区就会一直增长甚至会用尽TempDb的空间,就像长事务可以用尽事务日志空间一样。关于这一点我们会在稍后详述。 和UPDATE操作一样,如果行被删除也会创建行版本。