将列从可为空更改为不可为空会导致创建新列,操作被完全记录,并且如果您使用 RCSI,还会导致生成行版本。
您可以查看此主题以获取更多信息:Why does ALTER COLUMN to NOT NULL cause massive log file growth?
保护
我可以理解是否有另一个引用旧的连接
my.Table 中的(未更改的)行,但绝对不是这种情况。
您误解了 RSCI 的工作原理。
一旦完成向 RCSI 的转换,每次更新都会生成行版本,这与是否存在对这些行感兴趣的其他事务无关
当 READ_COMMITTED_SNAPSHOT 或 ALLOW_SNAPSHOT_ISOLATION
数据库选项为 ON,维护逻辑副本(版本)
在数据库中执行的所有数据修改。每次一行都是
由特定事务修改,数据库的实例
引擎存储该行先前提交的图像的一个版本
在临时数据库中。每个版本都标有交易序号
进行更改的交易。修改行的版本
使用链接列表链接。始终存储最新的行值
在当前数据库中并链接到存储在
临时数据库。
Understanding Row Versioning-Based Isolation Levels
或者更清楚地写成here:
当 READ_COMMITTED_SNAPSHOT 或 ALLOW_SNAPSHOT_ISOLATION
数据库选项为 ON,更新和删除事务
特定数据库必须维护行版本即使没有
使用基于行版本控制的隔离级别的事务。
使用行版本构建一致的数据快照涉及
系统资源(CPU 和内存),并可能产生 I/O
活动。因为记录版本存储在 tempdb 中,
当更多时,性能更好,发出的 I/O 的数量更少
tempdb 页面可以存储在内存中以进行行版本控制。
正如您想象的那样,ALTER TABLE 在 1 个事务中运行,因此行版本在此事务的所有持续时间内都处于活动状态(它们可以存活得更久,直到执行对它们感兴趣的语句,但由于没有人感兴趣,最小的“预期寿命”是拥有交易的持续时间)
..................................................... ..................................
更新:
我试图在 SQL Server 2012 上重现该问题:
我将 tempdb autogrowth 设置为 0(tempdata 设置为 10Mb,templog 设置为 1Mb)并创建了一个 20Mb 数据文件 + 10 Mb 日志文件的新数据库,简单的恢复模型,并创建了一个表 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 更改为 not null:
alter table dbo.nums alter column n bigint not null
这花了 0 秒,在此操作之前我的表大小约为 16Mb,它仍然保持在 16Mb 左右,没有日志文件增长,我将在图片中显示日志文件的内容。
然后我删除了表,重新创建并更改了我的数据库:
alter database rcsi set read_committed_snapshot on;
并且做了完全相同的事情:检查点 + 更改表 + 从 sys.fn_dblog() 中选择
我不得不等待 5 分钟,但 tempdb 没有给出错误。
在语句执行期间有 PREEMPTIVE_OS_GETDISKFREESPACE 作为等待类型,但猜猜它是什么。
它不是 tempdb(只有 10Mb + 1Mb 并且与我限制它的大小相同),它是我的用户数据库的 LOG FILE,它只是用于将数据类型从可为空更改为不可为空UNDER RCSI,已增长到 1Gb (!!!!)
1Gb 的日志用于更改仅 16Mb 表的 1 列的可空性
我一直在等待的不是 tempdb 增长,而是为我的 db 日志文件减少 1 Gb。
我附上了在 RC 和 RCSI 下同一操作期间记录的内容的图片,因此您可以看到生成行版本对用户数据库的成本比对 tempdb 的成本高得多,所以我认为您等待的时间都花在了将行版本记录到数据库日志文件中(它们根本没有记录到 tempdb 中)
Becides COPY_VERSION_INFO,有很多行修改可能不是您的情况:我的行有一个新的 14 字节行版本标记,因此对该表进行了太多更改,因为我在更改可空性之前更改了隔离级别,但在我的案例中,主要影响是由用户 db 日志文件增长产生的,而不是由根本没有增长的 tempdb 产生的。
附:也许您最好将此问题移至 dbaexchange?