本节摘要:一个数据库至少由一个主数据文件和一个事务日志文件组成,数据按 8KB 页与 64KB 区两级单位落盘。本节讲清这四层物理结构(数据库、文件组、文件、页)以及自动增长机制的运维红线。理解了页,第 8 章"这个查询读了几千页"的告警才有解构的语法。
接手新环境时先看物理文件布局,这是办案习惯。一个用户数据库至少两个文件:主数据文件存元数据与数据,可以再加次数据文件摊开 I/O;事务日志文件独立记账,读写模式与数据文件完全不同——数据文件是随机读写为主,日志文件几乎纯顺序追加。所有文件属于某个文件组,主文件组是默认驻地,你可以自建文件组把大表或大索引搬过去,让冷热数据物理隔离。查询当前布局:
SELECT name AS 文件名, type_desc AS 类型, -- ROWS 数据 / LOG 日志 size * 8 / 1024 AS 大小MB, growth AS 增长量, is_percent_growth AS 按百分比增长, physical_name AS 物理位置名 FROM sys.database_files; -- type_desc=ROWS 是数据文件,LOG 是日志; -- growth 看清单位:is_percent_growth 为 1 时是百分比,0 时是页数(8KB 一页)。
区是分配层的单位:八个页捆成一区,共 64KB。小对象早期从混合区里借页,长到八页后改用统一区整块分配。这解释了一个入门级疑问——"一行几十字节的小表,为什么也占几十 KB 磁盘":它至少占一页,而一页属于一个区。
每个 8KB 页分三段。页头 96 字节,记录页号、页类型、剩余空间等元信息;中间是数据行区,行从前往后紧密排布,一页数据区上限约 8060 字节;页尾是行偏移数组,从后往前生长,记录每行的位置。这个"两头向中间"的结构决定了几个工程事实:单行越长,一页装的行越少,扫描同样数据要读更多页;行溢出的大字段(VARCHAR MAX 类)另存到专门的溢出页,读取代价更高;页的填充程度(填充因子)决定了后续插入会不会频繁拆页产生碎片。
⚠️ 页不是行的容器那么随意:一次单行更新也要把整页调入缓冲池。理解这点后,"宽表让缓冲池命中率变差"就不难理解了——同样 10GB 热数据,行越宽占的页越多,前厅越快被挤满。
页有类型分工:数据页、索引页、文本溢出页,还有一组全局管理页(GAM、SGM、PFS)负责记录哪些区哪些页已被分配——它们是数据库"盘库"的底账。日常不需要直接读页,但排查页级损坏时,DBCC CHECKDB 的输出、页定位函数都会用页号(形如文件号比页号)说话。
文件默认按"自动增长"扩容。两种坑最常见:其一,按百分比增长的大库——一个 100GB 的数据文件按 10% 增长,一次扩容就要瞬间初始化 10GB 空间,期间整个库的写入近乎停摆,症状是"每天定时卡一下";其二,增长步长太小——每次扩 1MB,高频小扩容带来文件碎片与日志 VLF 碎片(3.3 节展开)。实践口径:数据文件按固定大小增长,步长设为几十 GB 或预计月增量的量级;提前规划容量、在维护窗口手动扩容,好过让业务高峰替你触发增长。
一次真实办案:某系统每晚两点规律性超时,排障三天无果,最后在默认跟踪里看到 AUTO_GROW 事件与超时时间完全吻合——日志文件每次只长 64MB,大事务一来就连续扩容几十次。把日志增长步长改成 8GB 并提前扩足后,超时消失。这类问题在现象层是"慢",在物理层是"文件在临时抱佛脚"。
tempdb 值得单独点名。它是实例级共享的临时仓库:排序溢写、哈希溢写、版本存储、临时表都住在里面,实例重启就重建。生产实践是给足初始大小、多数据文件摊并发页分配(一般按逻辑核数到八至十六个之间取值)、把监控告警对齐到 tempdb 使用率——它是全书出现频率最高的"公共背锅侠",很多看似业务库的故障根子在 tempdb。
物理结构聊完,必须补上"页坏了怎么办"的预防学。SQL Server 提供两级页校验:页校验和(页面写入时计算校验值存入页头,读回时验证)与残缺页检测(记录页的扇区位图防半页写入)。页面校验是 2005 之后的默认且应永远保持——它把"静默的数据腐烂"变成"可报警的错误"。配合两道工序构成早期发现体系:每周跑一次完整性检查(DBCC CHECKDB,代价高可拆分到库级别分摊),以及监控可疑页表(msdb 的 suspect_pages)——它记录引擎运行中发现的疑似损坏页,出现增长就是黄色警报。发现坏页的标准动作是"从备份还原该页或整库",绝不用第三方工具硬修——页级修复的前提是有干净的备份,这也再次呼应第 6 章:没演练过还原的完整性保护等于不存在。
顺带回答一个高频疑问:为什么查询偶尔报错重跑又好了?多数是存储层瞬时故障(虚拟化超卖、链路抖动)叠加页校验误伤面——如果同一页反复报校验错误,那就是真损坏,立即进入恢复流程;偶发一次且不再复现,先查底层存储的健康日志再定结论。
问:一个文件组里多个文件有什么讲究? 引擎按填充比例把数据摊到各文件,多文件的意义是摊开物理设备的并行度——同盘多文件没有收益只有管理成本,跨盘才有意义;各文件大小要均衡,尺寸悬殊会让摊布失效(小的先满,大的独扛)。
问:数据文件要不要预留空间? 要。按月增量提前扩容是免费的性能保险——自动增长不是不能有,而是不该在业务高峰被触发。季度巡检里看一眼文件剩余比例,低于两成就在维护窗口手动扩,十秒钟的事,省掉的是某天凌晨的定时卡顿。
货架结构清楚了。同一批货,怎么摆决定找货速度——下一节讲数据的两种"住法":堆与索引,以及索引设计的取舍艺术。