我和我的老老板打赌。我跟她打赌,SQL Server 在分配新范围时,总是从缓冲池中分配,并且从不检查磁盘上是否有可以存储分配的位置。本质上,她认为 SQL Server 应该在分配页面之前检查 LUN 上的可用空间。这似乎是错误的,因为我可以将我的存储放在月球上,这会导致一些严重的延迟。我觉得她真的希望 SQL Server 总是先从磁盘中读取一个页面,然后再执行 DML 任务。
这是我证明她错了的“证据”。如果您不同意我的“证明”,那么请务必以更好的方式回应!
让我们创建一个简单的数据库和表。数据库的恢复模式将被设置为 SIMPLE 并且 AUTO_CREATE_STATISTICS 将被关闭,以最小化日志记录膨胀。
在我们开始之前,让我透露一下我正在使用的 SQL Server 的版本。
选择@@版本; ---------------------------------------------- ---------------------------------- 微软 SQL Server 2012 - 11.0.2100.60 (X64) Windows NT 6.1(内部版本 7601:Service Pack 1)上的开发人员版(64 位)
现在,代码...
使用大师; 去 IF DATABASEPROPERTYEX(N'PageAllocDemo' , N'Version') > 0 开始 ALTER DATABASE PageAllocDemo SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE PageAllocDemo; 结尾; 去 创建数据库 PageAllocDemo 去 使用 PageAllocDemo; 去 设置无计数; 去 -- 将数据库设置为 SIMPLE 并关闭日志生成 crapola ALTER DATABASE PageAllocDemo SET RECOVERY SIMPLE; 去 改变数据库 PageAllocDemo 设置 AUTO_CREATE_STATISTICS OFF; 去 创建表 dbo.X ( c1 整数标识 (1,1) ) 在 [主要] 上; 去
现在,让我们检查一下分配了多少页?我怀疑是零,因为我们只创建了一个“逻辑表”,在我们的例子中是一个空堆。
-- 我们的表分配了多少页? DBCC IND (PageAllocDemo,X,-1); 去
现在,清除日志。
-- 清除日志 检查点; 去
日志中当前有什么?
-- 现在日志中有什么? SELECT * FROM fn_dblog(NULL,NULL); 去 /* -------------------------------------- -- 操作 -------------- 上下文 --- -------------------------------------- LOP_BEGIN_CKPT LCX_NULL LOP_XACT_CKPT LCX_BOOT_PAGE_CKPT LOP_END_CKPT LCX_NULL */
这是预料之中的,因为我们处于 SIMPLE 恢复模型中。我们现在将创建一个显式事务,它将在我们的表中插入一条记录;但是,在我们这样做之前,让我们打开 Process Monitor 并过滤我们的 MDF 和 LDF 文件以及 SQL Server 进程的 PID。
开始交易:
开始交易 插入 dbo.X 默认值; 去
Process Monitor 显示两次写入事务日志文件。
让我们检查日志记录。
-- 现在日志中有什么? SELECT * FROM fn_dblog(NULL,NULL); /* 我省略了PFS、GAM、SGAM等的所有日志记录。 ---------------------------------------------- -------------- -- 操作 -------------- 上下文 ------ 事务 ID --- ---------------------------------------------- -------------- LOP_BEGIN_XACT LCX_NULL 0000:0000030e LOP_BEGIN_XACT LCX_NULL 0000:0000030f LOP_FORMAT_PAGE LCX_HEAP 0000:0000030f LOP_COMMIT_XACT LCX_NULL 0000:0000030f LOP_INSERT_ROWS LCX_HEAP 0000:0000030e LOP_COMMIT_XACT LCX_NULL 0000:0000030e */
我省略了位图和 PFS 分配,我们可以看到分配了一个页面,并按预期插入了一行。
有多少页分配给我们的堆?
-- 我们的表分配了多少页? DBCC IND (PageAllocDemo,X,-1); 去 /* 一个 IAM 页面和一个数据页面,仅此而已 ---------------------------------- PageFID PagePID IAMFID IAMPID ------ ---------- ------ ------ 1 264 空 空 1 231 1 264 */
这是预料之中的。我们有一个 IAM 页面和一个数据页面。现在,我们的倒数第二个动作是提交交易。我预计此时会发生 512B 日志块刷新。
提交交易;
让我们用检查点操作来完成“证明”。到目前为止,没有任何内容提交到数据文件,只提交到日志文件。
检查点; 去
很酷,数据页按预期刷新到磁盘。
我的结论是,从 Process Monitor 的证据来看,SQL Server 在内存中分配,在内存中添加记录,并将页面提交到磁盘,所有这些都没有在存储级别检查任何内容。
有人反对这个假设吗?如果是,为什么?
为对象分配新范围时无需检查磁盘空间。SQL Server 已经拥有该磁盘空间。它知道其数据文件中的哪些页面已分配,哪些未分配,因此无需验证我们是否拥有我们已经知道的范围内的页面。它只会在缓冲池中分配空间并将数据写入内存,然后在检查点发生时覆盖磁盘上该空间中的任何内容。
SQL Server 唯一会费心查看磁盘上有多少可用空间的时间是在它实际扩展数据文件时。