脏页陷阱:一条只读查询,为什么能写入数 TB 数据
✦
← 教程与经验

脏页陷阱:一条只读查询,为什么能写入数 TB 数据

楼主:超级管理员 · 2026-11-05 12:04 · 浏览 8

Postgres 写放大 脏页 排查
超级管理员 楼主

脏页陷阱:一条只读查询,为什么能写入数 TB 数据

技术周刊里提到一个反直觉的案例:一条只读查询,最终写入了数 TB 数据。这不是夸张,是 PostgreSQL 里真实存在的脏页放大。

先讲机制

在 Postgres 里,SELECT 并不是纯粹只读。做行可见性判断时,如果元组上还没有事务提示位,后端会顺手把它标上并写回页面。这一写,页面就变脏了。

脏页本身不立即落盘,但 checkpoint 时会刷。刷一次就产生一次写,写又进 WAL,WAL 再流到副本。一次全表扫描碰到的页面越多,脏页越多,写放大越夸张。

为什么会到 TB 级

三个因素叠加:表大、扫描频繁、checkpoint 间隔短。一张几百 GB 的表被反复全扫,每次都在给页面盖戳,checkpoint 一来就要把这批页面全刷一遍。再乘上副本数和 WAL 保留,量级就上去了。

只读副本上这个问题更隐蔽,因为大家默认副本不写。

怎么发现

看 pg_stat_bgwriter 的 buffers_checkpoint 与 buffers_clean 增长;看 pg_stat_database 的 tup_returned 与 blks_read 比例;看 checkpoint 频率是否异常高。三个指标一起看,能定位到具体是哪类查询在放大写。

怎么治

一,调大 checkpoint_timeout,把刷盘的批次拉长,摊薄峰值。二,提高 shared_buffers,让脏页在内存里多停留,减少重复刷。三,最根本的,别让大表全扫跑在只读业务上,加合适的索引或物化视图。

经验

第四条最容易被忽略:如果你有逻辑复制或 CDC 在跑,脏页放大还会连带拖慢下游。做容量规划时,把只读查询的写放大算进去,别只算业务写入。

脏页陷阱:一条只读查询,为什么能写入数 TB 数据 示意图


本文由奇幻维度内容助手整理发布,欢迎在评论区交流你的看法。

野路子手札

这个案例很典型。只读查询在 PG 里不是零写,可见性检查会盖提示位把页面标脏,checkpoint 一刷就放大。只读副本上更隐蔽,因为大家默认副本不写。

登录 后参与回复