理解 Postgres 行大小

问题描述 投票:0回答:2

我有一个大的(>100M行)Postgres表,其结构为{整数,整数,整数,没有时区的时间戳}。我预计一行的大小为 3*integer + 1*timestamp = 3*4 + 1*8 = 20 bytes。

实际上,行大小为

pg_relation_size(tbl) / count(*)
= 52 字节。为什么?

(不对表进行任何删除:

pg_relation_size(tbl, 'fsm')
~= 0)

postgresql database-design types storage
2个回答
60
投票

行大小的计算比这更复杂。

存储通常按 8 kB 的“数据页”进行分区。每页有一个小的固定开销,可能的余数不够大,无法容纳另一个元组,更重要的是死行或最初通过 FILLFACTOR 设置保留的百分比。

还有更多的开销

每行

(元组):页面开头的 4 个字节的项目标识符、23 个字节的 HeapTupleHeader

对齐填充。元组头的开头以及元组数据的开头以 MAXALIGN 的倍数对齐,在典型的 64 位机器上为 8 个字节。某些数据类型需要与下一个 2、4 或 8 字节的倍数对齐。

引用系统桌上的手册

pg_tpye



typalign

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

可能的值为:

  • c

    =

    char
    对齐,即无需对齐。
    
    

  • s

    =

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

  • i

    =

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

  • d

    =

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

阅读手册中的
基础知识

你的例子

这会在 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

在页面标题中加上每个元组的项目标识符(如@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'); 以获取有关表和元组大小的更多信息。

相关:

表格尺寸与页面布局
  • PostgreSQL 中计算和节省空间
每一行都有与其关联的元数据。正确的公式是(假设简单对齐):

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

或者大约 53 字节。实际上,我专门编写了
postgresql-varint
来帮助解决这个确切用例的问题。您可能需要查看

类似的帖子以获取有关元组开销的更多详细信息。

© www.soinside.com 2019 - 2024. All rights reserved.