为什么表的数据空间可能会占用原始数据大小的 4 倍?

Jon*_*des 18 sql-server-2008 disk-space

我有一个包含 490 M 行和 55 GB 表空间的表,所以每行大约 167 个字节。该表包含三列: a VARCHAR(100)、 aDATETIME2(0)和 a SMALLINTVARCHAR字段中文本的平均长度约为 21.5,因此原始数据每行应约为 32 个字节: 22+2 表示VARCHAR, 6 表示DATETIME2,2 表示 16 位整数。

请注意,上面的空间仅是数据,而不是索引。我正在使用属性下报告的值 | 存储 | 一般 | 数据空间。

当然肯定会有一些开销,但是每行 135 个字节似乎很多,尤其是对于大表。为什么会这样?有没有其他人见过类似的乘数?哪些因素会影响所需的额外空间量?

为了进行比较,我尝试创建一个包含两个INT字段和 1 M 行的表。所需的数据空间为 16.4 MB:每行 17 个字节,而原始数据为 8 个字节。另一个测试表中的 anINT和 aVARCHAR(100)填充了与真实表相同的文本,每行使用 39 个字节(44 K 行),我预计 28 加上一点。

所以生产表有更多的开销。这是因为它更大吗?我希望索引大小大约为 N * log(N),但我不明白为什么实际数据所需的空间是非线性的。

在此先感谢您的指点!

编辑:

列出的所有字段都是NOT NULL. 真实的表在VARCHAR字段和DATETIME2字段上有一个聚集的 PK ,按这个顺序。对于这两个测试,第一个INT是(集群)PK。

如果重要:该表是 ping 结果的记录。这些字段是 URL、ping 日期/时间和以毫秒为单位的延迟。数据不断追加,从不更新,但数据会定期删除,以将其减少到每个 URL 每小时只有几条记录。

编辑:

这里一个非常有趣的答案表明,对于具有大量读写的索引,重建可能没有好处。就我而言,消耗的空间是一个问题,但如果写入性能更重要,那么使用松弛的索引可能会更好。

Mar*_*ith 11

经过对原问题的评论中的讨论,在这种情况下,丢失的空间似乎是由于选择了clustered key造成的,从而导致了大量碎片。

在这些情况下,始终值得通过 sys.dm_db_index_physical_stats 检查碎片状态。

编辑:在评论更新后

平均页面密度(在重建聚集索引之前)为 24%,这完全符合原始问题。这些页面只有 1/4 满,所以总大小是原始数据大小的 4 倍。


gbn*_*gbn 7

磁盘结构有开销:

  • 行标题
  • 空位图 + 指针
  • 可变长度列偏移
  • 行版本指针(可选)
  • ...

取 2 x 4 字节 int 列,你有

  • 4 字节行标题
  • 指向 NULL 位图的 2 字节指针
  • 2 个 int 列的 8 个字节
  • 3 字节 NULL 位图

哇 17 个字节!

您可以对第二个测试表进行相同的操作,它的开销与原始表一样:

  • 2 个字节用于可变长度列的计数
  • 每个可变长度列 2 个字节

为什么会有差异?另外(我不会链接到这些)

  • 你有没有重建索引对它们进行碎片整理?
  • 删除不回收空间
  • 如果插入中间,数据页将分裂
  • 更新可能会导致前向指针(留下间隙)
  • 行溢出
  • 删除了没有索引重建或 DBCC CLEANTABLE 的 varchar 列
  • 堆或表(堆没有聚集索引 = 记录分散在各处)
  • RCSI 隔离级别(每行额外 14 个字节)
  • varchar 中的尾随空格(默认情况下 SET ANSI_PADDING 为 ON)。使用 DATALENGTH 来检查,而不是 LEN
  • 使用 sp_spaceused 运行 @updateusage = 'true'
  • ...

请参阅:SQL Server:如何创建一个填充 8 KB 页的表?

来自 SO:


Aar*_*and 5

数据类型是否随时间变化?是否删除了可变长度列?是否经常对索引进行碎片整理但从未重建?是否删除了大量行或大量可变长度列被显着更新?