PostgreSQL toast 存储技术

1. 为什么需要 toast 技术

PostgreSQL 表数据的存储以页作为基本单位,默认情况下,一个页面 8kb 大小,pg 不允许一条记录跨多个页面,那么当一条记录的大小超过 8kb 时怎么处理?这就需要引入 toast 技术,英文全称为 The OverSized Attribute Storage Technique(超尺寸字段存储技术)。主要思路是使用额外的 toast 表来存储大字段数据,在原来的字段存储区内存储对应的 toast 表数据的 id。

2. 特定的字段类型才能使用 toast 机制

\d+ table_name

可以查看表字段的 Storage,分为如下几种类型:

  • plain:避免压缩或行外存储
  • main:允许压缩,不允许行外存储
  • external:允许行外存储,不允许压缩
  • extended:允许压缩和行外存储

对于可变长度的字段,其 Storage 通常为 extended 或者 external,可变长度的字段可通过 toast 机制存储大字段类型。

3. 普通表与 toast 表的关联

查询 pg_class 表,其 reltoastrelid 就是 toast 表的 oid,toast 表的表名后缀为普通表的 oid,举个例子:

普通表 t,oid 为 10022077,reltoastrelid 为 10022080

toast 表为:pg_toast_10022077,oid 为 10022080。

普通表与 toast 表在 pg_class 中通过字段 relkind 与可以区别,普通表为 'r',toast 表为 't'

4. 普通表的大字段 与 toast 表记录关联

  • external 的字段数据的第 1 个字节必须为 0x01
  • extended 的字段数据的第 1 个字节的低 2 位不能全部为 0,低 1 位表示是否 short 存储,低 2 位表示是否可压缩

源码参考宏定义:

  • VARATT_IS_EXTERNAL
  • VARATT_IS_EXTENDED

对于使用 toast 存储的字段,通常使用宏 VARATT_IS_EXTERNAL_ONDISK 来对字段数据进行判断,即第 1 个字节为 0x01,第 2 个字节为 0x12,后面是一个 varatt_external 结构,如下:

typedef struct varatt_external { int32 va_rawsize; /* Original data size (includes header) */ int32 va_extsize; /* External saved size (doesn't) */ Oid va_valueid; /* Unique ID of value within TOAST table */ Oid va_toastrelid; /* RelID of TOAST table containing it */ } varatt_external;