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


18

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

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

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

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

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

在此先感谢您提供任何指导!

编辑:

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

如果很重要:该表记录了ping结果。这些字段是URL,Ping日期/时间和延迟(以毫秒为单位)。数据会不断添加,并且永远不会更新,但是会定期删除数据,以使每个URL每小时仅减少几条记录。

编辑:

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

Answers:


11

在对原始问题的评论中进行讨论之后,在这种情况下,似乎丢失的空间是由于选择群集密钥而导致的,导致了巨大的碎片化。

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

编辑:评论中的更新

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


7

磁盘上的结构有开销:

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

取2 x 4个字节的int列,

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

哇17个字节!

您可以为第二个测试表使用相同的表,这比您原来的表具有更多的开销:

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

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

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

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

从SO:


2x4字节int列样本不是100%正确。您将拥有4个字节的行标题(2个状态字节和2个字节的固定长度数据大小)。然后,您将有2x4字节的数据。两个,而不是17个字节用于列计数和空位图的单个字节,给15个字节的总记录长度
马克S. Rasmussen的

@Mark S. Rasmussen:在哪里可以获得“固定长度数据大小为2个字节”?MSDN?和零位始终是3个字节:sqlskills.com/blogs/paul/post/... + msdn.microsoft.com/en-us/library/ms178085%28v=sql.90%29.aspx
GBN

哇,好细节!我VARCHAR在上面的估算中考虑了s 的长度字段,但没有考虑列数。该表没有可空字段(应该提到),它是否仍为它们分配字节?
所有行业的乔恩

重建索引会影响所需空间的数据部分吗?也许重建聚集索引会。插入确实会发生在中间,尽管如果我交换了将停止的聚类字段的顺序,中间也会发生很多。其余大多数都不适用于这种情况,但是对于一般情况是很有用的。我会检查您的链接。好东西!
所有行业的乔恩

1
@gbn固定长度数据大小的2个字节是您提到的4字节行标题的一部分。这是指向固定数据长度部分的结尾/列计数的开头/空位图的指针。NULL位图并不总是三个字节。如果您包括列数,那么它将至少为三个字节,但可能会更多—我在描述中拆分了位图和列数。此外,虽然在这种情况下会出现NULL位图,但并不总是存在。
Mark S. Rasmussen

5

数据类型是否随时间变化?变长列是否已删除?索引是否经常进行碎片整理但从未重建过?是否删除了很多行,或者是否大量更新了可变长度列?这里有一些很好的讨论。


我有97%的信心没有更改数据类型或删除字段。如果我这样做了,那表的行数要少得多,那真是太早了。没有删除或更新,仅附加数据。
所有行业的乔恩

更正:有删除,并颇有几分。该表的净增长非常可观,因此我想可以很快地重用此空间。
所有行业的乔恩

大量删除后,数据可能会也可能不会被重用。该表的集群键是什么?插入是在表格的中间还是结尾?
mrdenny

集群键在VARCHARDATETIME2字段上按此顺序复合。插入内容将平均分配给第一个字段。对于第二个字段,新值和将始终大于任何现有值。
所有行业的乔恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.