我有一个大的(>100M行)Postgres表,其结构为{整数,整数,整数,没有时区的时间戳}。我预计一行的大小为 3*integer + 1*timestamp = 3*4 + 1*8 = 20 bytes。
实际上,行大小为
pg_relation_size(tbl) / count(*)
= 52 字节。为什么?
(不对表进行任何删除:
pg_relation_size(tbl, 'fsm')
~= 0)
行大小的计算比这更复杂。
存储通常按 8 kB 的“数据页”进行分区。每页有一个小的固定开销,可能的余数不够大,无法容纳另一个元组,更重要的是死行或最初通过 FILLFACTOR
设置保留的百分比。
(元组):页面开头的 4 个字节的项目标识符、23 个字节的 HeapTupleHeader
和
MAXALIGN
的倍数对齐,在典型的 64 位机器上为 8 个字节。某些数据类型需要与下一个 2、4 或 8 字节的倍数对齐。引用系统桌上的手册pg_tpye
:
基础知识typalign
是存储此类值时所需的对齐方式。 它适用于磁盘上的存储以及大多数表示形式 PostgreSQL 内部的值。当存储多个值时 连续地,例如在完整行的表示中 磁盘,填充被插入到这种类型的数据之前,以便它 从指定的边界开始。对齐参考是 序列中第一个数据的开始。
可能的值为:
阅读手册中的
c
=
对齐,即无需对齐。char
s
=
对齐(大多数机器上为 2 个字节)。short
i
=
对齐(大多数机器上为 4 个字节)。int
d
=
对齐(许多机器上为 8 个字节,但绝不是全部)。double
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
在页面标题中加上每个元组的项目标识符(如@A.H.在评论中指出的那样):
+ 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来帮助解决这个确切用例的问题。您可能需要查看
类似的帖子以获取有关元组开销的更多详细信息。