25

我有一个大的(> 100M 行)Postgres 表,其结构为 {integer, integer, integer, timestamp without time zone}。我预计行的大小为 3*integer + 1*timestamp = 3*4 + 1*8 = 20 字节。

实际上,行大小为pg_relation_size(tbl) / count(*)= 52 字节。为什么?

(没有对表进行删除:pg_relation_size(tbl, 'fsm')~= 0)

4

2 回答 2

54

行大小的计算比这复杂得多。

存储通常划分为 8 kB数据页。每页有一个小的固定开销,可能的剩余部分不足以容纳另一个元组,更重要的是死行或最初与FILLFACTOR设置一起保留的百分比。

每行(元组)还有更多开销:页面开头的 4 个字节的项目标识符、HeapTupleHeader23 个字节和对齐填充。元组头的开始和元组数据的开始以 的倍数对齐,MAXALIGN在典型的 64 位机器上是 8 个字节。某些数据类型需要与 2、4 或 8 字节的下一个倍数对齐。

引用系统表上的手册pg_tpye

typalign是存储此类型值时所需的对齐方式。它适用于磁盘上的存储以及 PostgreSQL 中值的大多数表示。当多个值连续存储时,例如在磁盘上表示完整行时,在这种类型的数据之前插入填充,以便它从指定的边界开始。对齐参考是序列中第一个基准的开始。

可能的值为:

  • c=char对齐,即不需要对齐。

  • s=short对齐(在大多数机器上为 2 个字节)。

  • i=int对齐(大多数机器上为 4 个字节)。

  • d=double对齐(在许多机器上为 8 个字节,但绝不是全部)。

在此处阅读手册中的基础知识。

你的例子

这会在 3integer列之后产生 4 字节的填充,因为该timestamp列需要double对齐并且需要从下一个 8 字节的倍数开始。

所以,一行占用:

   23   -- heaptupleheader
 +  1   -- padding or NULL bitmap
 + 12   -- 3 * integer (no alignment padding here)
 +  4   -- padding after 3rd integer
 +  8   -- timestamp
 +  0   -- no padding since tuple ends at multiple of MAXALIGN

加上页眉中每个元组的项目标识符(如注释中的@AH 指出的那样):

 +  4   -- item identifier in page header
------
 = 52 bytes

所以我们得到了观察到的52 个字节

计算pg_relation_size(tbl) / count(*)是悲观估计。pg_relation_size(tbl)包括膨胀(死行)和保留的空间fillfactor,以及每个数据页和每个表的开销。(我们甚至没有提到TOAST 表varlena中长数据的压缩,因为它不适用于这里。)

您可以安装附加模块pgstattuple并调用SELECT * FROM pgstattuple('tbl_name');有关表和元组大小的更多信息。

有关的:

于 2012-11-26T18:23:42.023 回答
5

每行都有与之关联的元数据。正确的公式是(假设天真的对齐):

3 * 4 + 1 * 8 == your data
24 bytes == row overhead
total size per row: 23 + 20

或大约 53 个字节。实际上,我专门编写了postgresql-varint来帮助解决这个确切用例的问题。您可能想查看类似的帖子以获取更多详细信息:元组开销。

于 2012-11-26T18:15:49.603 回答