我有一个大的(> 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)
我有一个大的(> 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)
行大小的计算比这复杂得多。
存储通常划分为 8 kB数据页。每页有一个小的固定开销,可能的剩余部分不足以容纳另一个元组,更重要的是死行或最初与FILLFACTOR
设置一起保留的百分比。
每行(元组)还有更多开销:页面开头的 4 个字节的项目标识符、HeapTupleHeader
23 个字节和对齐填充。元组头的开始和元组数据的开始以 的倍数对齐,MAXALIGN
在典型的 64 位机器上是 8 个字节。某些数据类型需要与 2、4 或 8 字节的下一个倍数对齐。
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');
有关表和元组大小的更多信息。
有关的:
每行都有与之关联的元数据。正确的公式是(假设天真的对齐):
3 * 4 + 1 * 8 == your data
24 bytes == row overhead
total size per row: 23 + 20
或大约 53 个字节。实际上,我专门编写了postgresql-varint来帮助解决这个确切用例的问题。您可能想查看类似的帖子以获取更多详细信息:元组开销。