SQL Server里那个关于表最小行到底咋定义的纠结,真是让人头大
- 问答
- 2026-01-11 06:19:13
- 5
这事儿说起来还真有点绕,源头是微软官方文档里有一句挺出名的话,大概意思是:“SQL Server 的数据库页大小是 8KB,但是一页最多只能放 8060 字节的数据。”这句话本身没问题,是铁打的事实,但问题就出在,很多人,包括一些有经验的开发者,会顺着这个逻辑往下想:一行数据的大小不能超过 8060 字节,否则一页就放不下了,那反过来,一行数据最小能有多小呢?是不是理论上可以非常非常小?那是不是意味着,我一个 8KB 的页就能塞进去海量的行?比如一行如果只有 10 个字节,那我是不是能塞进去 8060 / 10 ≈ 806 行?
这么一想,好像很合理对吧?但坑就从这里开始了,如果你真的去动手试,创建一个表,里面就一个 tinyint 类型的列(它只占 1 个字节),然后拼命往里插数据,你会发现,一页远远装不到 806 行!可能只能装个几百行就到头了,这时候你就会懵了,说好的 8060 字节呢?我这每行才 1 字节,就算加上一点点头部开销,离 8060 也差得远啊,怎么页就用完了?
这就是让人头大的核心纠结点:那个“8060字节”的限制,是针对“行内数据”的,但一页的容量,不仅仅被“行内数据”占用,还被一大堆你看不见的“行外开销”给瓜分掉了。 你把所有开销都算上,才能算出真正一页能放多少行。
具体是哪些开销在捣鬼呢?根据 SQL Server 的存储引擎内部原理,主要有这么几座“大山”:

第一座大山是行头开销,每一行数据,除了你定义的列的值之外,SQL Server 都会在它前面加一个“头信息”,这个头里包含了很多管理信息,比如有多少个列、是否有变长列、是否有 NULL 位图、是否有版本控制信息(比如开了快照隔离)等等,这个行头的大小不是固定的,通常是至少 4 个字节,但如果你的表结构复杂点(比如有很多可空列),它可能轻松涨到十几甚至几十个字节,你算最小行的时候,光数据用 1 字节,行头可能就用了 5 字节,实际每行占位一下就变成了 6 字节起步。
第二座大山是行偏移数组,这个是最容易被忽略,但也最“占地方”的,在每一个数据页的末尾,SQL Server 不会像我们想象的那样把行一条紧挨着一条地放,相反,它会在页的末尾维护一个“目录”,这个目录就是一个由“槽”组成的数组,每个槽占 2 个字节,用来记录对应行在页面内的起始位置(偏移量)。关键点来了:页面上每存在一行,无论这一行多小,都会在末尾的这个数组里占用一个 2 字节的槽。 这意味着什么?意味着即使你的行数据本身小到只有 1 字节,它也为这一页的总体占用“贡献”了 1字节(数据)+ 行头开销 + 2字节(槽位),这个 2 字节的槽位是铁打不动的,行再小也得占。
现在我们来重新算一下那个极限情况,假设行头开销最小是 4 字节,数据 1 字节,那么每行的“净占用量”是 4 + 1 = 5 字节,但同时,每一行还会在页尾固定消耗一个 2 字节的槽位,一个 8KB 的页是 8192 字节,但真正能用来存储“行数据+行头+偏移数组”的空间并不是 8192,还要减去页本身的一些头信息(96 字节),大概剩下 8096 字节可用。

如果我们设一页能放 N 行,那么总的空间消耗公式大致是:N * (行大小) + N * 2 <= 8096,代入我们的例子:N * (4+1) + N * 2 <= 8096 => 7N <= 8096 => N <= 1156,你看,这样算出来最大行数大约是 1156 行,虽然还是很多,但已经远低于最初天真计算的 806 行了(注意,这里因为行更小,所以最大行数反而变多了,但依然受限于偏移数组),由于还有其他细微开销,真实值会比这个理论值稍小一些。
但纠结还没完!还有第三座大山:填充因子,即使你算清了上面的账,你创建表或索引时设置的 FILLFACTOR 也会直接影响每页初始填充的行数,如果你设置 FILLFACTOR = 80,就意味着 SQL Server 会有意只把页填满到 80%,为未来的更新预留空间,这又会进一步减少一页里实际存放的行数。
回到最初的问题“表最小行到底咋定义的?”其实这个问题本身就有歧义,它可能指:
- 单行数据的最小可能大小:这个受系统数据类型限制,理论上可以非常小(比如就一个
bit列)。 - 一页能容纳的绝对最大行数:这个不单取决于行数据大小,更取决于“行数据 + 行头 + 偏移数组”这个综合体的总大小,它是一个通过计算得出的理论极值。
- 一个实际表中一页的平均行数:这个就更复杂了,它受表结构(有无变长列、可空列)、是否开启某些数据库特性(如版本控制)、填充因子设置、以及数据更新模式等多种因素影响。
让人头大的根源在于,我们直觉上认为“页大小除以行大小”就是最大行数,但 SQL Server 的存储引擎为了实现高效、可靠的管理,在每一行和每一页都添加了必不可少的“管理费”,这些隐藏的成本,使得实际情况与简单算术相去甚远,当你试图去深究那个“最小”或“最大”的精确数字时,就会发现它不是一个固定的魔法数字,而是一个随着表结构、数据库设置动态变化的、需要综合计算的复杂结果,这种理想与现实的差距,就是纠结感的来源。
本文由盈壮于2026-01-11发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/78528.html
