TOAST 内部机制与大值存储
了解 PostgreSQL 如何存储超大列,并调整压缩和外部存储的阈值。
TOAST 内部机制与大值存储 是 CoddyKit 上的免费 PostgreSQL Performance & Query Optimization 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 PostgreSQL Performance & Query Optimization 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 PostgreSQL Performance & Query Optimization 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Why TOAST Exists
PostgreSQL stores rows on fixed-size 8 KB pages. A single row cannot span multiple pages, so a wide value (a long text, big jsonb, or bytea) would never fit.
TOAST (The Oversized-Attribute Storage Technique) solves this by compressing oversized columns and, if still too large, slicing them into chunks stored in a separate side table.
- Keeps the main heap row small and cache-friendly.
- Lets a logical value far exceed 8 KB (up to ~1 GB).
- Happens automatically and transparently to your queries.
The TOAST Threshold
TOAST kicks in when a row's total size would exceed TOAST_TUPLE_THRESHOLD, which is 2 KB (one quarter of the 8 KB page) by default.
When that limit is crossed, PostgreSQL compresses and/or moves the largest toastable attributes out of line until the row fits under TOAST_TUPLE_TARGET (also ~2 KB).
Only columns of variable-length types (text, varchar, jsonb, bytea, arrays, etc.) are toastable. Fixed-width types like integer or timestamptz are never toasted.
Finding the TOAST Table
Every table with at least one toastable column gets an associated TOAST table named pg_toast.pg_toast_<oid>. You can locate it from the catalog.
The reltoastrelid column links a heap relation to its TOAST relation; a value of 0 means no TOAST table was created.
SELECT c.relname,
c.reltoastrelid,
t.relname AS toast_table
FROM pg_class c
LEFT JOIN pg_class t ON t.oid = c.reltoastrelid
WHERE c.relname = 'documents';The Four Storage Strategies
Each column has a storage strategy controlling whether it can be compressed and/or moved out of line:
PLAIN— no compression, no out-of-line; only valid for non-toastable types.EXTENDED— allow both compression and out-of-line storage (default for most varlena types).EXTERNAL— allow out-of-line but no compression (faster substring access).MAIN— allow compression but keep in the main table unless absolutely necessary.
Inspecting Per-Column Storage
Query pg_attribute.attstorage to see each column's strategy. The codes map to: p=PLAIN, e=EXTERNAL, m=MAIN, x=EXTENDED.
SELECT attname,
atttypid::regtype AS type,
CASE attstorage
WHEN 'p' THEN 'plain'
WHEN 'e' THEN 'external'
WHEN 'm' THEN 'main'
WHEN 'x' THEN 'extended'
END AS storage
FROM pg_attribute
WHERE attrelid = 'documents'::regclass
AND attnum > 0
AND NOT attisdropped;Changing a Column's Strategy
Use ALTER TABLE ... SET STORAGE to override the default. A common optimization: if you frequently read random substrings of a large bytea (e.g. range reads on a blob), switch to EXTERNAL so values are stored uncompressed and can be partially fetched without decompressing the whole thing.
The new strategy applies only to rows written after the change; existing data is not rewritten until updated.
ALTER TABLE documents
ALTER COLUMN payload SET STORAGE EXTERNAL;
-- Force a rewrite to apply it to existing rows:
VACUUM FULL documents;Compression Algorithms: pglz vs lz4
PostgreSQL compresses TOAST values before considering out-of-line storage. Two algorithms are available:
pglz— the historic built-in, decent ratio, slower.lz4— available since PostgreSQL 14, much faster compress/decompress with a slightly lower ratio. Requires the server to be built with lz4 support.
The cluster-wide default is set by default_toast_compression.
SHOW default_toast_compression;
-- Set lz4 cluster-wide (postgresql.conf or per session):
SET default_toast_compression = 'lz4';Per-Column Compression
From PostgreSQL 14 onward you can set the compression method on individual columns with SET COMPRESSION. This is independent of the storage strategy.
Choose lz4 for hot, large columns where CPU during read/write matters; keep pglz or use EXTERNAL when ratio or substring access dominates.
ALTER TABLE documents
ALTER COLUMN body SET COMPRESSION lz4;
-- Inspect chosen method per column:
SELECT attname, attcompression
FROM pg_attribute
WHERE attrelid = 'documents'::regclass
AND attnum > 0;Tuning the Out-of-Line Target
The reloption toast_tuple_target controls how aggressively PostgreSQL pushes attributes out of line: it sets the size the main tuple is shrunk toward (valid range 128 bytes to ~8160).
Lowering it makes more values go to TOAST sooner, keeping the main heap dense and improving scan performance when the big columns are rarely read. Raising it keeps more data inline.
ALTER TABLE documents
SET (toast_tuple_target = 512);
-- Verify current reloptions:
SELECT reloptions
FROM pg_class
WHERE relname = 'documents';Measuring TOAST Footprint
Use the size functions to separate main-table bytes from TOAST bytes. This tells you whether your bloat lives in the heap or in oversized values.
pg_table_size— heap + TOAST + their indexes' TOAST, excluding regular indexes.pg_relation_size(rel, 'main')— just the main fork.
SELECT
pg_size_pretty(pg_relation_size('documents')) AS heap,
pg_size_pretty(
pg_total_relation_size(reltoastrelid)
) AS toast,
pg_size_pretty(pg_total_relation_size('documents')) AS total
FROM pg_class
WHERE relname = 'documents';Practical Performance Implications
TOAST is invisible until it isn't. Key effects to remember:
- Reading a toasted column triggers extra index lookups into the TOAST table and possible decompression — avoid
SELECT *when you only need small columns. - Values stay TOASTed across UPDATEs that don't touch them, so unrelated updates are cheap.
EXTERNALenables efficientsubstr()/ range reads on large uncompressed blobs.- Switching
pglztolz4can cut write CPU dramatically on insert-heavy large-value workloads.
Quick Check
You have a table whose large bytea column is read mostly via substr() on small byte ranges, and these reads are slow because each access decompresses the entire value. Which single change best fixes this?
Recap
You learned how PostgreSQL handles oversized values:
- TOAST triggers when a row would exceed the ~2 KB threshold, compressing then moving large varlena columns into
pg_toast.*tables. - Four strategies —
PLAIN,MAIN,EXTENDED(default),EXTERNAL— control compression and out-of-line placement, set viaALTER TABLE ... SET STORAGE. lz4(PG14+) offers faster compression thanpglz; choose per column withSET COMPRESSIONor cluster-wide viadefault_toast_compression.toast_tuple_targettunes how eagerly values leave the heap; size functions reveal how much storage lives in TOAST.- Pick
EXTERNALfor substring/range reads,lz4for write-heavy large values, and avoidSELECT *to skip needless detoasting.
常见问题解答
「TOAST 内部机制与大值存储」课时是免费的吗?
是的 — 「TOAST 内部机制与大值存储」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 PostgreSQL Performance & Query Optimization 课程的其余内容,请升级到 CoddyKit PRO。 PostgreSQL Performance & Query Optimization 课程共包含 4 节课。
「TOAST 内部机制与大值存储」这节课中我会学到什么?
了解 PostgreSQL 如何存储超大列,并调整压缩和外部存储的阈值。 你通过在浏览器中直接运行的动手代码来练习 PostgreSQL Performance & Query Optimization,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 PostgreSQL Performance & Query Optimization 需要有经验吗?
无需任何先前经验。CoddyKit 上的 PostgreSQL Performance & Query Optimization 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「TOAST 内部机制与大值存储」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 PostgreSQL Performance & Query Optimization 课中编写并运行代码吗?
能。每节 PostgreSQL Performance & Query Optimization 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 准确测量表膨胀与索引膨胀
- 使用 pg_repack 回收空间
- 调整高频更新表的填充因子
- TOAST 内部机制与大值存储