计算元组大小

Calculate tuple size

我试图了解列顺序如何最小化 PostgreSQL 中的 table 大小。

示例:

CREATE TABLE test (
 column_1 int
,column_2 int
,column_3 bigint
,column_4 bigint
,column_5 text
,column_6 text
,column_7 numeric(5,2)
,column_8 numeric(5,2)
,column_9 timestamp
,column_10 boolean
,column_11 boolean
);

INSERT INTO test
  VALUES(1,1,1,1,'test','test_1',12,12,current_timestamp,true,false);

SELECT pg_column_size(test.*) FROM test;

 pg_column_size 
----------------
       82
    (1 row)

元组大小:

元组头的 23 字节开销 + NULL 位图的 1 字节,因此:

24+4+4+8+8+5+7+5+5+8+1+1=80 但实际的元组大小是 82。

有没有2字节的额外开销?

我理解下面给出的例子 link:
Calculating and saving space in PostgreSQL

如果我们删除 column_8 numeric(5,2) 那么元组大小也保持不变,即:82.

我已重新排序 table 以最小化元组大小并给出 80。

CREATE TABLE test (
 column_3 bigint
,column_4 bigint
,column_9 timestamp
,column_1 int
,column_2 int
,column_10 boolean
,column_11 boolean
,column_7 numeric(5,2)
,column_8 numeric(5,2)
,column_5 text
,column_6 text);

INSERT INTO test
  VALUES(1,1,current_timestamp,1,1,true,false,12,12,'test','test_1');

SELECT pg_column_size(test) FROM test;

 pg_column_size 
----------------
      80

对于 PostgreSQL 中的列顺序有什么建议吗?

您在 column_9 timestamp 之前错过了另外 2 个字节的填充,它需要从 8 个字节的倍数开始。

24+4+4+8+8+5+7+5+5+8+1+1=80 but the actual tuple size is 82.  
------------------^ <----- 2 bytes of padding here

也是这个原因:

If we remove the column_8 numeric(5,2) then also the tuple size remains the same, i.e.: 82.

取出占用 5 个字节的列后,您会在同一位置得到 7 个字节的填充 - 最坏的情况。

另请注意,这一行实际上在磁盘上占用 88 字节,因为下一个元组的元组头是左对齐的(从 MAXALIGN 的倍数开始,通常为 8 个字节)。

您修改后的行以 8 字节的倍数结尾,不会产生额外的填充,只需要 80 个字节。

不过,两者都需要另外 4 个字节用于页眉中的元组指针。

这是一个 "column tetris" 的游戏,你似乎已经了解了它的基础知识。通常你不会得到太多,不要想太多。不过,也有极端的极端情况。空值改变游戏 每行 .

您需要了解每种数据类型的大小、对齐和填充要求以及 NULL 位图的特殊规则。

  • Do nullable columns occupy additional space in PostgreSQL?

dba.SE上详细计算的相关答案:

这在 MS-SQL 中使用 sys.objectssys.columns 计算非常简单,我相信 PostgreSQL 中一定有一些类似的解决方案:

/* Total Size of Tuple */ select sum(c.max_length) [Total Tuple Size] from sys.columns c inner join sys.objects o on c.object_id = o.object_id inner join sys.types t on c.user_type_id = t.user_type_id where o.name = 'your-table-name'

/* Column wise byte size */ select c.name [Column Name] ,t.name [Type] ,c.max_length [Maximum Length in Bytes] from sys.columns c inner join sys.objects o on c.object_id = o.object_id inner join sys.types t on c.user_type_id = t.user_type_id where o.name = 'your-table-name' order by c.column_id