Sql server 为什么改变表格。。。更改列。。。在TempDB中填充版本存储

Sql server 为什么改变表格。。。更改列。。。在TempDB中填充版本存储,sql-server,sql-server-2012,alter-table,tempdb,Sql Server,Sql Server 2012,Alter Table,Tempdb,我必须将一个大表中的BIGINT列从可为null更改为不可为null ALTER TABLE my.Table ALTER COLUMN myColumn BIGINT NOT NULL 在我们的UAT和RC环境中运行此程序大约需要3个小时,并发活动水平较低。UAT和RC都是PROD的反映,因此是良好的测试平台。考虑到桌子的大小和套件的性能,3小时是合理的 据我所知,相关的配置是snapshot\u isolation\u state=0,is\u read\u committed\u sna

我必须将一个大表中的BIGINT列从可为null更改为不可为null

ALTER TABLE my.Table ALTER COLUMN myColumn BIGINT NOT NULL
在我们的UAT和RC环境中运行此程序大约需要3个小时,并发活动水平较低。UAT和RC都是PROD的反映,因此是良好的测试平台。考虑到桌子的大小和套件的性能,3小时是合理的

据我所知,相关的配置是snapshot\u isolation\u state=0,is\u read\u committed\u snapshot\u on=1

在其他活动开始失败后,ALTER TABLE在PROD中被终止了几次(在运行了几个小时后,然后进行了长时间的回滚)“访问数据库“MyDatabase”中表“myOther.table”中的版本化行时,事务中止。找不到请求的版本化行。您的tempdb可能空间不足。请参阅BOL,了解如何配置tempdb进行版本控制。“错误

第三次在PROD中运行此程序时,我安排关闭所有其他活动。大约4个小时后,很明显有些东西不起作用。使用中的初始查询,我可以看到版本存储区中大部分是TempDB,但ALTER TABLE连接没有被阻塞,CPU和IO增长缓慢,因此我确信它可以正常运行在我活着的时候,我看到的唯一等待就是SOS_调度器_产量。没有其他非琐碎的连接

又过了几个小时,我决定给TempDB添加一些空间。ALTER表很快就完成了


有人能解释一下ALTER表为什么会暂停吗?我能理解是否有另一个连接引用了旧的(未更改的)my.Table中的行,但情况绝对不是这样。

将列从可为null更改为不可为null会导致创建新列,操作会被完全记录,如果使用RCSI,还会导致生成行版本

有关详细信息,请查看此主题:

规整

我能理解是否有另一个连接引用了旧的 (未更改)我的.Table中的行,但绝对不是这样

你误解了RSCI的工作原理

一旦完成到RCSI的转换,每次更新都将生成行版本,而不考虑是否存在对这些行感兴趣的Otheret事务

当读取\u提交\u快照或允许\u快照\u隔离时 数据库选项处于打开状态,为其维护逻辑副本(版本) 在数据库中执行的所有数据修改。每次修改一行时 由特定事务修改的数据库实例 引擎存储以前提交的行映像的版本 在tempdb中。每个版本都标记有事务序列号 进行更改的事务的。已修改行的版本 使用链接列表链接。始终存储最新的行值 在当前数据库中,并链接到存储在 tempdb

或者更清楚地说,它是这样写的:

当读取\u提交\u快照或允许\u快照\u隔离时 数据库选项为打开、更新和删除事务 特定数据库必须维护行版本,即使没有 使用基于行版本控制的隔离级别的事务。 使用行版本构建一致的数据快照涉及 系统资源(CPU和内存),并可能生成I/O 活动。因为记录版本存储在tempdb中, 当数量越多,性能越好,发布的I/O数量越少 tempdb页面可以存储在内存中以进行行版本控制

正如您所想象的,ALTER TABLE在一个事务中运行,因此行版本在该事务的所有持续时间内都处于活动状态(它们可能会持续更长时间,直到对它们感兴趣的语句执行为止,但由于没有人对此感兴趣,所以最小的“预期寿命”是所属事务的持续时间)

更新:

我试图在SQL Server 2012上重现该问题:

我将tempdb autogrowth设置为0(tempdata设置为10Mb,temlog设置为1Mb),创建了一个新的数据库,其中包含20Mb的数据文件+10Mb的日志文件,简单的恢复模型,并创建了一个表dbo.Nums,其中填充了1000000个整数(bigint,null),方法如下:

select top 1000000 row_number() over(order by 1/0) as n
into dbo.Nums
from sys.all_columns c1 cross join sys.all_columns c2;
然后我做了一个检查点,并将一列从null改为notnull:

alter table dbo.nums alter column n bigint not null
这花费了0秒,在执行此操作之前,我的表大小约为16Mb,它保持在16Mb左右,没有日志文件增长,我将在图片中显示日志文件的内容

然后我放下桌子,重新创建它,并更改了我的数据库:

alter database rcsi set read_committed_snapshot on;
并且做了完全相同的事情:checkpoint+altertable+select from sys.fn_dblog()

我不得不等待5分钟,但tempdb没有给出任何错误。 在语句执行期间,有
抢占式\u OS\u GETDISKFREESPACE
作为等待类型,但是猜猜它是什么。 它不是tempdb(只有10Mb+1Mb,与我限制的大小相同),而是我的用户数据库的日志文件,它只是为了在RCSI下将数据类型从可空更改为不可空,已增长到1Gb(!!)

1Gb日志,用于更改仅为16Mb的表的1列的可空性 我一直在等待的不是tempdb的增长,而是为我的db日志文件消耗1GB

我附上了在RC和RCSI下的相同操作期间记录的内容的图片,因此您可以看到,生成行版本对用户数据库的成本要比对tempdb的成本高得多,因此我认为您等待的时间花在了将行版本记录到数据库日志文件上(它们根本没有记录在tempdb中)

因为复制了版本信息,所以有许多行修改可能不是您的情况:我的行有一个新的14字节行版本标记,所以有太多的人