行版本控制资源的使用情况

行版本控制框架支持 SQL Server 中的下列功能:

  • 触发器

  • 多个活动的结果集 (MARS)

  • 联机索引

另外,行版本控制框架还支持下列基于行版本控制的事务隔离级别(默认情况下禁用):

  • READ_COMMITTED_SNAPSHOT 数据库选项为 ON 时,READ_COMMITTED 事务通过使用行版本控制提供语句级读取一致性。

  • ALLOW_SNAPSHOT_ISOLATION 数据库选项为 ON 时,SNAPSHOT 事务通过使用行版本控制提供事务级读取一致性。

基于行版本控制的隔离级别通过消除对读取操作使用共享锁来减少事务获取的锁数目。这样就减少了管理锁所用资源,从而提高了系统性能。另外还减少了其他事务获取的锁阻塞事务的次数,也就提高了性能。

基于行版本控制的隔离级别增加了数据修改所需的资源。启用这些选项会导致要复制数据库中要修改的所有数据。即使没有使用基于行版本控制隔离的活动事务,也将修改前的数据备份在 tempdb 中。修改后的数据包括一个指向存储在 tempdb 中的修改前的数据的指针。对于大型对象,只将对象中更改过的部分复制到 tempdb 中。

tempdb 中使用的空间

对于每个数据库引擎实例,tempdb 都必须具有足够的空间以容纳在该实例中为每个数据库生成的行版本。数据库管理员必须确保 tempdb 具有足够的空间来支持版本存储区。tempdb 中有两种版本存储区:

  • 联机索引生成版本存储区,用于所有数据库中的联机索引生成操作。

  • 公共版本存储区,用于所有数据库中的所有其他数据修改操作。

只要活动事务需要访问行版本,就必须存储行版本。后台线程每隔一分钟删除一次不再需要的行版本,从而释放 tempdb 中的版本空间。如果长时间运行的事务符合下列任何一种条件,则会阻止释放版本存储区中的空间:

  • 使用基于行版本控制的隔离。

  • 使用触发器、MARS 或联机索引生成操作。

  • 生成行版本。

注意注意

在事务内部调用了触发器后,即使触发器完成后不再需要行版本,由触发器创建的行版本将仍然受到维护直到事务结束。这也同样适用于使用行版本控制的已提交读事务。对于这种事务类型,只有事务中的每条语句需要数据库的事务一致视图。这表示语句完成后将不再需要在事务中为它创建的行版本。但是,由事务中的每条语句创建的行版本将受到维护到事务完成。

当 tempdb 运行空间不足时,数据库引擎强制收缩版本存储区。在收缩进程期间,尚未生成行版本且运行时间最长的事务被标记为牺牲品。在错误日志中为每个作为牺牲品的事务生成消息 3967。如果某个事务被标记为牺牲品,则该事务不能再读取版本存储区中的行版本。当其尝试读取行版本时,会生成消息 3966 且该事务会被回滚。如果收缩进程成功,则 tempdb 中就有可用空间。否则 tempdb 运行空间不足,并出现下列情况:

  • 写操作继续执行但不生成版本。错误日志中会生成一条信息消息 (3959),但写数据的事务不受影响。

  • 尝试访问由于 tempdb 完全回滚而未生成的行版本的事务终止,并生成错误消息 3958。

数据行中使用的空间

每个数据库行的结尾处最多可以使用 14 个字节记录行版本控制信息。行版本控制信息包含提交版本的事务的事务序列号和指向版本行的指针。如果符合下列任何一种条件,则第一次修改行时或插入新行时添加这 14 个字节:

  • READ_COMMITTED_SNAPSHOT 或 ALLOW_SNAPSHOT_ISOLATION 选项为 ON。

  • 表有触发器。

  • 正在使用多个活动的结果集 (MARS)。

  • 当前正在对表执行联机索引生成操作。

如果符合下列所有条件,则第一次修改数据库行时,将从行中删除这 14 个字节:

  • READ_COMMITTED_SNAPSHOT 和 ALLOW_SNAPSHOT_ISOLATION 选项为 OFF。

  • 表不再有触发器。

  • 当前没有使用 MARS。

  • 当前没有执行联机索引生成操作。

如果使用了行版本控制功能,则可能需要为数据库分配额外的磁盘空间,才能使每个数据库行可多使用 14 个字节。如果当前页上没有足够的可用空间,则添加行版本控制信息会导致拆分索引页或分配新的数据页。例如,如果平均行长度为 100 个字节,则额外的 14 个字节会导致现有表增大 14%。

减少填充因子可能有助于避免或减少索引页碎片。若要查看表或视图的数据和索引的碎片信息,可以使用 DBCC SHOWCONTIG

大型对象中使用的空间

SQL Server 数据库引擎支持六种数据类型(最多可以容纳大小为 2 GB 的大型字符串):nvarchar(max)、varchar(max)、varbinary(max)、ntext、text 和 image。使用这些数据类型的大型字符串存储在一系列与数据行链接的数据片段中。行版本控制信息存储在用于存储这些大型字符串的每个片段中。数据片段是表中专用于大型对象的页集合。

新的大型值添加到数据库中时,系统会为它们分配数据片段,每个片段最多可以存储 8040 个字节的数据。早期版本的数据库引擎中,每个片段最多可以存储 8080 个字节的 ntext、text 或 image 数据。

数据库从早期版本的 SQL Server 升级到 SQL Server 时,现有的 ntext、text 和 image 大型对象 (LOB) 数据并未更新来为行版本控制信息释放一些空间。但第一次修改 LOB 数据时,该数据会动态升级以实现版本控制信息的存储。即使未生成行版本也是如此。LOB 数据升级后,每个片段最多可以存储的字节数从 8080 个减少到 8040 个。升级过程相当于先删除 LOB 值再重新插入相同值。即使只修改一个字节也会升级 LOB 数据。对于每个 ntext、text 或 image 列,这是一次性操作,但每个操作可能生成大量页分配和 I/O 活动,具体情况取决于 LOB 数据的大小。如果完整记录修改,还会生成大量日志记录活动。如果数据库恢复模式未设置为 FULL,则按最小方式记录 WRITETEXT 操作和 UPDATETEXT 操作。

在早期版本的 SQL Server 中不使用 nvarchar(max)、varchar(max) 和 varbinary(max) 数据类型。因此,这些数据类型不存在升级问题。

应该分配足够的磁盘空间来满足此要求。

监视行版本控制和版本存储区

为了监视行版本控制、版本存储区和快照隔离进程以了解性能和问题,SQL Server 提供了一些工具,包括动态管理视图 (DMV) 和 Windows 系统监视器中的性能计数器。

DMV

下列 DMV 提供有关 tempdb 的当前系统状态、版本存储区以及使用行版本控制的事务的信息。

sys.dm_db_file_space_usage。返回数据库中每个文件的空间使用量信息。有关详细信息,请参阅 sys.dm_db_file_space_usage (Transact-SQL)

sys.dm_db_session_space_usage。返回针对数据库的会话中进行的页分配和释放活动。有关详细信息,请参阅 sys.dm_db_session_space_usage

sys.dm_db_task_space_usage。返回针对数据库的任务中进行的页分配和释放活动。有关详细信息,请参阅 sys.dm_db_task_space_usage

sys.dm_tran_top_version_generators。返回一个虚拟表,其中包含生成的版本是版本存储区中最多的对象。该表按 database_id 和 rowset_id 对前 256 位的聚合记录长度进行分组。可以使用此函数来查找版本存储区的最大使用者。有关详细信息,请参阅 sys.dm_tran_top_version_generators

sys.dm_tran_version_store。返回一个虚拟表,其中包含公共版本存储区中的所有版本记录。有关详细信息,请参阅 sys.dm_tran_version_store

注意注意

由于 sys.dm_tran_top_version_generators 和 sys.dm_tran_version_store 都查询整个版本存储区(可能非常大),因此运行这两者时可能要占用大量资源。

sys.dm_tran_active_snapshot_database_transactions。返回一个虚拟表,其中包含使用行版本控制的 SQL Server 实例中的所有数据库中的所有活动事务,但系统事务不会显示在此 DMV 中。有关详细信息,请参阅 sys.dm_tran_active_snapshot_database_transactions

sys.dm_tran_transactions_snapshot。返回一个虚拟表,其中包含每个事务使用的快照。该快照包含了使用行版本控制的活动事务的序列号。有关详细信息,请参阅 sys.dm_tran_transactions_snapshot

sys.dm_tran_current_transaction。返回一行,其中包含当前会话中与行版本控制相关的事务状态信息。有关详细信息,请参阅 sys.dm_tran_current_transaction

sys.dm_tran_current_snapshot。返回一个虚拟表,其中包含当前快照隔离事务启动时的所有活动事务。如果当前事务正在使用快照隔离,则此函数不返回行。sys.dm_tran_current_snapshot 与 sys.dm_tran_transactions_snapshot 类似,只是它仅返回当前快照的活动事务。有关详细信息,请参阅 sys.dm_tran_current_snapshot

性能计数器

SQL Server 性能计数器提供有关受 SQL Server 进程影响的系统性能的信息。下列性能计数器监视 tempdb、版本存储区以及使用行版本控制的事务。这些性能计数器包含在 SQLServer:Transactions 性能对象中。

Free Space in tempdb (KB)。监视 tempdb 数据库中的可用空间 (KB)。tempdb 中必须有足够的可用空间来容纳支持快照隔离的版本存储区。

下列公式可以用来粗略估计版本存储区的大小。对于长时间运行的事务,监视生成速率和清除速率对于估计版本存储区的最大大小会非常有用。

[公共版本存储区的大小] = 2 * [每分钟生成的版本存储区数据] * [事务的最长运行时间(分钟)]

事务的最长运行时间不应该包括联机索引生成时间。对于超大型表,由于这些操作可能要花很长的时间,因此联机索引生成使用单独的版本存储区。当联机索引生成处于活动状态时,联机索引生成版本存储区的近似大小等于表(包括所有索引)中修改的数据量。

Version Store Size (KB)。监视所有版本存储区的大小 (KB)。此信息有助于确定版本存储区在 tempdb 数据库中所需的空间大小。监视计数器一段时间,可以获得有用的信息来估计在 tempdb 数据库中所需的额外空间。

Version Generation rate (KB/s)。监视所有版本存储区中的版本生成速率(KB/秒)。

Version Cleanup rate (KB/s)。监视所有版本存储区中的版本清除速率(KB/秒)。

注意注意

Version Generation rate (KB/s) 和 Version Cleanup rate (KB/s) 的信息可以用于预测 tempdb 空间要求。

Version Store unit count。监视版本存储区单元的计数。

Version Store unit creation。监视自启动实例后创建用于存储行版本的版本存储区单元总数。

Version Store unit truncation。监视自启动实例后被截断的版本存储区单元总数。当 SQL Server 确定不需要任何存储在版本存储区单元中的版本行来运行活动事务时,版本存储区单元即被截断。

Update conflict ratio。监视存在更新冲突的更新快照事务与更新快照事务总数的比值。

Longest Transaction Running Time。监视使用行版本控制的事务的最长运行时间(秒)。这可用于确定是否存在事务的运行时间不合适的情况。

Transactions。监视活动事务的总数,不包括系统事务。

Snapshot Transactions。监视活动快照事务的总数。

Update Snapshot Transactions。监视执行更新操作的活动快照事务的总数。

NonSnapshot Version Transactions。监视生成版本记录的活动非快照事务的总数。

注意注意

Update Snapshot Transactions 与 NonSnapshot Version Transactions 之和表示参与版本生成的事务的总数。Snapshot Transactions 与 Update Snapshot Transactions 之差表示只读快照事务数。