我有一个大的(> 100M行)Postgres表,其结构为{integer,integer,integer,timestamp without time zone}。我期望一行的大小为3 *整数+ 1 *时间戳= 3 * 4 + 1 * 8 = 20字节。
实际上,行大小为pg_relation_size(tbl) / count(*)
= 52个字节。为什么呢?
(对表格没有删除:pg_relation_size(tbl, 'fsm')
〜= 0)
答案 0 :(得分:44)
行大小的计算要复杂得多。
存储通常以8 kB 数据页进行分区。每页有一个小的固定开销,可能的余数不足以容纳另一个元组,更重要的是死行或最初用FILLFACTOR
设置保留的百分比。
还有更多的开销每行(元组):页面开头的4个字节的项标识符,23个字节的HeapTupleHeader
和 对齐填充 。元组头的开始以及元组数据的开始以MAXALIGN
的倍数对齐,在典型的64位机器上为8字节。某些数据类型需要与下一个2个,4个或8个字节的倍数对齐。
Quoting the manual on the system table pg_tpye
:
typalign
是存储此类型值时所需的对齐方式。 它适用于磁盘上的存储以及磁盘的大多数表示 PostgreSQL中的值。存储多个值时 连续地,例如在完整行的表示中 磁盘,填充在此类型的数据之前插入以便它 从指定的边界开始。对齐参考是 序列中第一个数据的开头。可能的值是:
c
=char
对齐,即无需对齐。
s
=short
路线(大多数机器上有2个字节)。
i
=int
对齐(大多数机器上为4个字节)。
d
=double
路线(许多机器上有8个字节,但绝不是全部)。
阅读手册here中的基础知识。
这会在3 integer
列后产生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
加上占用4个字节的页眉中的每个元组的项标识符(如pointed out by @A.H. in the comment):
+ 4 -- item pointer in page header
------
= 52 bytes
所以我们到达观察到的 52字节。
计算pg_relation_size(tbl) / count(*)
是一种悲观估计。 pg_relation_size(tbl)
包括膨胀(死行)和fillfactor
保留的空间,以及每个数据页和每个表的开销。 (我们甚至没有在TOAST tables中提及长varlena
次数据的压缩,因为它不适用于此处。)
您可以安装附加模块pgstattuple并致电SELECT * FROM pgstattuple('tbl_name');
以获取有关表格和元组大小的更多信息。
相关:
答案 1 :(得分:3)
每一行都有与之关联的元数据。正确的公式是(假设天真对齐):
3 * 4 + 1 * 8 == your data
24 bytes == row overhead
total size per row: 23 + 20
大约53个字节。我实际上写了postgresql-varint专门用来帮助解决这个问题。您可能需要查看similar post以获取更多详细信息:tuple overhead。