PostgreSQL toast 存储技术

2023年 8月 15日 30.3k 0

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;

以上结构可以看出,一个 toast 字段,其存储的内容包括原始数据大小,外部数据大小,toast 表中数据的 chunk_id,toast 表的 oid。通过 oid,chunk_id,就能很容易的定位到字段数据。

toast 表中的数据通常是压缩的,判断是否压缩的方法是对比 va_rawsize 和 va_extsize 的大小,如下:

#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
((toast_pointer).va_extsize < (toast_pointer).va_rawsize - VARHDRSZ)

对于 toast 表,其单条记录也是有大小限制的,对于一个大字段,其在 toast 表中会对应多条记录,这些记录的 chunk_id 相同,但是 chunk_seq 不同。

看一个 toast 例子:
下面是一个普通表大字段存储的值:

\x0112191800002d0d0000c3ec9800c0ec9800

  • 0x0112 表示外部存储,即宏 VARATT_IS_EXTERNAL_ONDISK 对应的判断。
  • 0x19180000 表示 4 字节原始存储大小
  • 0x2d0d0000 表示 4 字节外部存储大小
  • 0xc3ec9800 表示 toast 表记录的 chunk_id
  • 0xc0ec9800 表示 toast 表的 oid

5. 涉及 toast 表的 DML 操作

对一个涉及大字段的普通表执行 DML 操作,普通表与 toast 表的 wal 写入顺序如下:

  • insert 操作,先 insert toast 表记录,再对普通表进行 insert 操作
  • update 操作,先 insert toast 表记录,再删除 toast 表记录,最后更新普通表。
  • delete 操作,先删除普通表记录,再删除 toast 表记录

以上可以看出,insert 与 update 都是先对 toast 表进行操作,然后对普通表操作。而 delete 则相反。

内核版本:pg12

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论