PostgreSQL高效建表存储数据对齐问题

磁盘上的数据布局与 RAM 中的数据表示完全一致。页面及其元组按原样读入缓冲区缓存中,无需任何转换。这就是为什么数据文件在不同平台之间不兼容的原因

  • 不兼容的原因之一字节序 。例如,x86 架构是小端序 ,z/Architecture 是大端序 ,而 ARM 的字节序是可配置的
  • 另一个原因是按照机器字边界进行数据对齐,这也是许多架构所要求的。例如,在 32 位 x86 系统中,整数 (integer 类型,占用四个字节) 按四字节字边界对齐,就像双精度浮点数 (double precision 类型,占用八个字节)。但在 64 位系统中,双精度数按八字节字边界对齐。

数据对齐使得元组的大小取决于表中字段的顺序。这种影响通常可以忽略不计,但在某些情况下,它会导致大小显著增加。

使用pageinspect 扩展中的 heap_page_items 函数来显示关于指针和元组的一些细节。

sql 复制代码
=> CREATE TABLE padding(
  b1 boolean,
  i1 integer,
  b2 boolean,
  i2 integer
);
=> INSERT INTO padding VALUES (true,1,false,2);
=> SELECT lp_len FROM heap_page_items(get_raw_page('padding', 0));
 lp_len
−−−−−−−−
     40
(1 row)
​
#这一行的大小是 40 个字节,其行头占 24 个字节,integer 类型的列占用 4 个字节,每个 boolean 类型的列占用 1 个字节。总共 34 个字节,因此浪费了 6 个字节用于整数列的四字节对齐。
​
#如果我们重建表,空间将被更有效地利用:
=> DROP TABLE padding;
=> CREATE TABLE padding(
  i1 integer,
  i2 integer,
  b1 boolean,
  b2 boolean
);
=> INSERT INTO padding VALUES (1,2,true,false);
=> SELECT lp_len FROM heap_page_items(get_raw_page('padding', 0));
 lp_len
−−−−−−−−
     34
(1 row)
​

在 PostgreSQL 中,表通常被称为堆 (heap)。这是另一个晦涩的术语,暗示了元组的空间分配和动态内存分配之间的相似性。确实可以看到某种类比,但表由完全不同的算法管理。与有序索引相比,我们可以将这个术语理解为"一切都堆积成堆"。

关键概念:字节对齐

  • 4 字节对齐:数据必须存储在能被 4 整除的内存地址上
  • 填充字节:为满足对齐要求而插入的空字节

第一种表结构(低效)

ini 复制代码
CREATE TABLE padding(
  b1 boolean,    -- 1 字节
  i1 integer,    -- 4 字节,需要 4 字节对齐
  b2 boolean,    -- 1 字节  
  i2 integer     -- 4 字节,需要 4 字节对齐
);
​
内存布局分析:
偏移量:  0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
内容:   [行头................................] [b1] [pad] [pad] [pad] [i1...........]
        
偏移量: 16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31
内容:   [b2] [pad] [pad] [pad] [i2...........] [未使用的空间...................]
​
总计: 24(行头) + 1(b1) + 3(填充) + 4(i1) + 1(b2) + 3(填充) + 4(i2) = 40 字节
​
浪费了6个字节

第二种表结构(高效)

sql 复制代码
CREATE TABLE padding(
  i1 integer,    -- 4 字节,4 字节对齐
  i2 integer,    -- 4 字节,4 字节对齐  
  b1 boolean,    -- 1 字节
  b2 boolean     -- 1 字节
);
​
内存布局分析:
偏移量:  0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
内容:   [行头................................] [i1...........] [i2...........]
        
偏移量: 16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31
内容:   [b1] [b2] [未使用的空间................................................]
​
总计: 24(行头) + 4(i1) + 4(i2) + 1(b1) + 1(b2) = 34 字节
​

PostgreSQL 在存储表行时,会根据每个数据类型的对齐要求插入填充字节以满足对齐规则。整数类型(integer)需要 4 字节对齐,因此如果布尔类型(boolean,1 字节)列放在整数列之前或中间,会产生填充字节浪费空间。

  • 原始表结构:布尔类型列散布在整数列中间,导致每个整数列前都可能插入填充字节,使行长度增加至 40 字节。
  • 重新排序字段:先放所有整数类型,再放布尔类型,避免了中间填充,数据紧凑排列,使行长度减少到 34 字节。

因此,合理设计列的顺序,以满足对齐要求,可以显著减少行占用的存储空间,提高存储效率和 IO 性能。

相关推荐
IvorySQL14 小时前
PG 日报|社区讨论重构 pg_hba 配置文件格式
数据库·人工智能·postgresql·重构·ivorysql
weixin_3077791319 小时前
Linux下Docker Compose里运行PostgreSQL数据库故障诊断Shell脚本
linux·运维·数据库·docker·postgresql
打工的小王20 小时前
PostgreSQL
数据库·postgresql
IvorySQL1 天前
PG 日报|新增 VFD 缓存监控视图,精细化数据库调优
大数据·数据库·人工智能·缓存·postgresql·区块链
禁默1 天前
没有公网IP也能连数据库:把内网PostgreSQL开放给远程联调
数据库·tcp/ip·postgresql·cpolar
howard20052 天前
PostgreSQL起步
数据库·postgresql
学习-学习2 天前
PostgreSQL 19 Beta 值得测吗?并行 Autovacuum、REPACK 到 WAIT FOR LSN 看生产影响
数据库·postgresql
何中应2 天前
Postgres 搭建主从集群
数据库·postgresql·集群·开发工具
IvorySQL4 天前
从双解析器到循环工程:IvorySQL 五年技术演进路线的深度观察
大数据·数据库·人工智能·postgresql·开源
jnrjian5 天前
PG 切换用户 \c -
数据库·postgresql