理解Postgres行的大小

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

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

(不对表执行删除操作: pg_relation_size(tbl, 'fsm') 〜= 0)

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

存储通常分为8 kb的数据页面 。 每页有一个小的固定开销,可能的余数不够大,不适合另一个元组,更重要的是死行或最初用FILLFACTOR设置保留的FILLFACTOR

更重要的是, 每行 (元组)有开销。 23字节的HeapTupleHeaderalignment填充 。 元组头的开始以及元组数据的开始以MAXALIGN的倍数alignment,这是一个典型的64位机器上的8个字节。 某些数据types需要alignment到2,4或8个字节的下一个倍数。

引用系统表中的手册pg_tpye

typalign是存储此types的值时所需的alignment方式。 它适用于磁盘上的存储以及PostgreSQL中的大多数表示forms。 当连续存储多个值时(例如在磁盘上的完整行的表示forms中),将在此types的数据之前插入填充,以便在指定的边界上开始填充。 alignment引用是序列中第一个数据的开始。

可能的值是:

  • c = charalignment,即不需要alignment。

  • s = shortalignment(大多数机器上2个字节)。

  • i = intalignment(大多数机器上4个字节)。

  • d = doublealignment(在许多机器上是8个字节,但不是全部)。

阅读手册中的基础知识。

你的例子

这将导致在3个integer列之后填充4个字节,因为timestamp列需要doublealignment,并且需要从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 

最后,页眉中每个元组都有一个ItemData指针(项目指针),它占用4个字节:

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

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

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

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

相关解答:

  • 表格大小与页面布局

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

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

或大致53个字节。 我实际上编写了postgresql-varint来帮助解决这个问题。 你可能想看看类似的post,了解更多的细节:元组开销。