← 返回题目列表

PostgreSQL 的 TOAST 是什么?大字段存储有什么影响?

高频 中等 第 5 / 31 题 更新于 2026/07/29
PostgreSQLTOAST大字段存储

简化版

TOAST 是 PostgreSQL 用来处理超大行和大字段的机制,会把过大的变长字段压缩或拆到旁边的 TOAST 表中存储。它让一行能保存大文本、JSONB、bytea,但大字段仍会增加 IO、缓存、Vacuum 和查询成本,列表查询应避免无谓读取大字段。

详细版

PostgreSQL 数据页通常是 8KB,一行太大时不能完整放入普通页。TOAST 会对变长字段进行压缩或外置存储,比如 textjsonbbytea

注意点:

  • TOAST 让大字段能存,不代表适合频繁读写。
  • 查询不需要大字段时,不要 select *
  • 大 JSONB 更新可能导致整值重写。
  • 大字段会影响表膨胀、Vacuum 和备份。
  • 图片、视频等文件多数情况下仍应放对象存储。

面试回答要区分:TOAST 是数据库内部存储优化,不是万能的大文件方案。

完整版教学

一、TOAST 解决的是行太大的问题

PostgreSQL 默认数据页大小常见为 8KB,如果一行包含很长的 textjsonb,普通页面放不下。TOAST 就是为这种超大变长字段设计的机制。

它会尝试压缩字段,压缩后仍然太大时,会把字段拆成块放到 TOAST 表里,主表行里保存引用。

主表行: id, title, content_pointer
TOAST 表: content chunk 1, chunk 2, chunk 3 ...

记忆钩子:TOAST 让大字段“能存下”,但不代表“大字段没有成本”。

二、哪些字段可能触发 TOAST

TOAST 主要作用于可变长度字段,比如 textvarcharjsonbbytea、数组等。短字段不会触发外置存储。

如果一篇文章正文 100KB,它很可能被压缩或拆到 TOAST 表;如果只是 100 字节标题,仍然在主表行内。

字段类型是否可能 TOAST常见场景
text可能文章正文、备注
jsonb可能扩展属性、配置
bytea可能二进制内容
int不会普通数值

所以设计表时要识别哪些字段是冷的大字段,避免它们影响热查询。

三、select 星号会放大大字段成本

如果查询只需要标题和状态,却写 select *,数据库可能需要取出大字段引用,甚至访问 TOAST 数据。即使某些场景会延迟取值,应用层传输和反序列化也可能被拖慢。

-- 列表页推荐
select id, title, status, created_at
from article
where status = 'published';

假设列表一次查 20 行,每行正文 50KB,误查正文就会多传约 1MB 数据。高并发下这不是小问题。

四、大 JSONB 更新可能很贵

JSONB 很灵活,但如果一个大 JSONB 字段里只改一个小属性,底层仍可能产生新的行版本和新的 TOAST 数据。PostgreSQL 的 MVCC 更新是生成新版本,不是在原地随便改几个字节。

extra 大小 200KB
只改 extra.color
可能仍产生新的行版本和 TOAST 写入

因此高频更新字段不要埋在大 JSONB 里。稳定且常更新、常查询的字段应该列化。

五、TOAST 会影响 Vacuum 和表膨胀

大字段更新会产生旧版本,TOAST 表也会产生需要清理的旧数据。如果长事务阻止 Vacuum 回收,主表和 TOAST 表都可能膨胀。

排查时不能只看主表大小,也要看相关 TOAST 表大小。一个看似不大的业务表,背后的 TOAST 表可能非常大。

select pg_size_pretty(pg_total_relation_size('article'));

这个函数看总大小,比只看主表 heap 更接近真实占用。

六、大文件仍然更适合对象存储

TOAST 支持 bytea,但图片、视频、PDF 等大文件通常不建议直接放数据库。数据库备份、复制、恢复、连接传输都会被大文件拖重。

更常见方案是对象存储保存文件内容,PostgreSQL 保存元数据和权限关系。

PostgreSQL: file_id, owner_id, storage_key, size, hash
对象存储: 真正文件内容

数据库擅长结构化数据和事务,对象存储擅长大对象存储和分发。

七、常见误区与追问

  • 误区:有 TOAST 就可以放心把所有大文件放库里。 TOAST 是内部存储机制,不解决大文件传输、备份和分发成本。
  • 误区:JSONB 改一个字段一定很轻。 大 JSONB 更新可能导致整值新版本和 TOAST 写入。
  • 误区:表不大就没有大字段问题。 TOAST 表可能占用大量空间,要看总关系大小。
  • 追问:为什么不要 select 星号? 可能把大字段带入查询路径,增加 IO、网络和反序列化成本。
  • 追问:文章正文要不要拆表? 高频列表不需要正文时,拆成正文表能隔离热冷字段。
  • 追问:如何查看表总大小? 使用 pg_total_relation_size 查看包含索引和 TOAST 的总大小。

八、面试中可以这样落地

如果设计文章表,可以主表保存标题、摘要、状态和时间,正文单独放正文表,列表页不查正文。

create table article (
  id bigint primary key,
  title text not null,
  summary text not null,
  status text not null
);

create table article_body (
  article_id bigint primary key,
  content text not null
);

如果是附件系统,则文件内容放对象存储,PostgreSQL 只存元数据。这样能把 TOAST 和对象存储的边界讲清楚。

九、加强记忆

TOAST 记住“压缩、外置、分块、仍有成本”。它解决大字段放不下的问题,不代表大字段读写便宜。面试时围绕 select *、JSONB 更新、Vacuum 膨胀和对象存储边界来回答,层次会很完整。