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 性能。

相关推荐
auspicious航11 小时前
PostgreSQL的备份与恢复
数据库·postgresql
IvorySQL12 小时前
PostgreSQL 日报| WAL summarizer 断言失败(10 月 26 日)
数据库·postgresql·区块链
ss27317 小时前
30 PostgreSQL ACID特性详解
数据库·postgresql
严同学正在努力18 小时前
PostgreSQL 数据库安装部署教程(Linux 多发行版 + Windows + Docker 全覆盖)
linux·数据库·postgresql
唐青枫20 小时前
PostgreSQL 基本操作详解
postgresql
风哥2号2 天前
数据库教程FGMT69:PostgreSQL运维诊断与监控分析
运维·数据库·postgresql
ss2732 天前
AI时代首先数据库-PostgreSQL实战笔记 27 PostgreSQL数据写入与读取流程解析
数据库·笔记·postgresql
瑞克-桑切斯2 天前
PostgreSQL 14.4 源码部署 & 运维手册(CentOS7)
数据库·学习·postgresql
ss2732 天前
AI时代首先数据库-PostgreSQL实战笔记 | 28 PostgreSQL MVCC机制介绍
数据库·笔记·postgresql
geovindu2 天前
sql:SQL Pagination and Limiting Results using PorstgreSQL 18.0
大数据·数据仓库·sql·postgresql·数据库开发·数据库架构·1024程序员节