aar*_*rkk 5 postgresql index postgresql-9.6
我一直在深入研究我们生产的 PostgreSQL 9.6 数据库的大小,并发现了一些我认为令人惊讶的结果。
我们有一个foos
包含大约1000 万条记录的表(我们称之为)。主键是一个integer
. 我们在这个表上有一个 B 树索引,用于将可选外键插入另一个表(我们称之为bars
)。让我们调用 index index_foos_on_bar_id
。bars.id
只是一个integer
数据类型列。
当我使用\di+
meta 命令查看索引大小时,我发现它占据了大约1GB的空间。一些粗略的计算意味着索引中的每个条目因此需要大约 1GB / 1000 万 =每行 100 字节的空间。
foos
表上几乎没有发生删除,所以膨胀是不存在的。
在我看来,索引将有效地包含诸如将索引列映射到相关表的主键的排序数字对之类的内容。但是,由于它只是integer
类型,因此每行仅使用大约 4 + 4 = 8 个字节,这与实际占用的每行 100 个字节相差甚远。我想它是一个树结构的事实可能会稍微提高一点,但超过 10 倍的差异对我来说有点令人惊讶。
索引使用的所有这些“额外”空间的原因是什么?
在 foos 表上几乎没有发生删除,所以膨胀是不存在的。
你不需要删除来膨胀。更新也会这样做。10,000,000 个整数上的新索引给了我 214MB 的大小,所以你确实有膨胀(或者正在使用奇怪的硬件,也许)。你不能仅仅通过考虑 to 来检测膨胀。重建索引并查看它是否变小,或使用pgstattuple。当然,拥有一个紧密打包的索引并不是很有效,因为几乎每个操作都需要进行页面拆分。
索引不指向主键值,它直接指向表。这称为“tid”,通常需要 6 个字节。
数据值本身存储为 8 字节对齐,因此即使您的数据是 int4,它仍然需要 8 字节的磁盘空间。由于这种对齐,您可以在两个 int4 列上构建索引,并且它仍将采用与一个 int4 列相同的大小。
PostgreSQL 总是准备好处理每一行的 NULL 值,以及可变长度的数据,即使表/索引的定义排除了这样的事情。所以每个索引元组都有一个标头,它告诉我们该行是否有任何 NULL 或可变长度数据,以及该元组的总长度。这会占用一些空间。
PostgreSQL 使用页面组织的存储,而不仅仅是一个巨大的 malloc 块。每个页面包含更多的开销,包括每个块和块内的每个条目(请参阅 typedef struct ItemIdData)。
因此,您将获得数据本身的 8 个字节(bar_id,对齐)、包含 tid 指针的元组标头的 8 个字节以及每个块内的行指针的 4 个字节,最多为 20 个字节。由于非叶页的一些开销加上每个块的开销以及块内由于对齐或由于它们位于数据的参差不齐而无法使用的一些空间,我得到了 22.4 字节的实际值。另一个 4.5 折将是膨胀(尽管一些“膨胀”可能是有用的可用空间,除非您的索引从现在开始绝对是静态的)