保留固定点扣住堆框中的叉号旧版本;释放后,同样大小的框出现可复用空格,尾部存活行仍在。

清理在可见性边界允许后回收旧版本

PostgreSQL 更新一行时会产生新版本,删除则让旧版本对后来的快照不可见。但这不意味着所有较早开始读取的事务都已经用完旧版本。普通清理,包括自动回收工作者发起的清理,都必须遵守这个可见性边界,之后才能复用空间。把清理任务安排得更频繁,不能越过仍然有效的读取快照。 坏掉的样子 一个持续很久的快照,可能在删除事务提交以后仍然需要旧行。因此,清理命令可以正常执行,却没有回收预期数量的版本。单纯降低自动回收触发门槛,也不会让这些版本突然变成可删除。应先检查长事务和其他持有回收边界的对象,例如复制槽保留的 xmin,再判断是不是工作者吞吐不足。 回收元组空间,通常意味着同一张表以后可以复用这些位置,并不必然让文件长度下降。普通清理可以截断文件末尾连续的空页,却不会把中间所有存活行搬到一起、整理掉每个孔洞。因此,清理之后磁盘文件大小不变,可能是正常的内部空间复用,不能仅凭这个现象宣布任务没有发挥作用。 示意格区分可见性、回收与复用。手动双会话夹具仍待运行验证;堆长度不变不说明清理失败。 快速验证 本轮没有重跑原样保留的双会话快照与清理实验。早先的 PostgreSQL 观察属于历史记录,单用户后端无法复现保留读者的边界。 准备同一个 PostgreSQL 18 可丢弃数据库,开启甲、乙两个全新的 psql -X -v ON_ERROR_STOP=1 会话。实验只在自己的测试表上暂时关闭自动回收,并手动执行普通清理以控制时序。它验证的是自动与手动清理共同遵守的可见性规则,不把手动命令当作自动调度已经运行的证据。使用尚不存在的实验模式,以及有权创建和维护其中表的角色;乙须保持自动提交,因为 VACUUM 不能在事务块内执行。乙先创建五千行数据: CREATE SCHEMA vacuum_demo; CREATE TABLE vacuum_demo.items (id integer PRIMARY KEY,payload text) WITH (autovacuum_enabled=false); INSERT INTO vacuum_demo.items SELECT g,repeat(md5(g::text),10) FROM generate_series(1,5000) AS g; SELECT pg_stat_force_next_flush(); VACUUM (ANALYZE) vacuum_demo.items; CREATE TABLE vacuum_demo.baseline AS SELECT pg_relation_size('vacuum_demo.items') AS heap_bytes; 统计刷新辅助函数让待发布计数在下一个机会写出,这里用于减少短实验中的观察滞后。基线记录的是堆文件字节数,不包括索引和其他分支文件。甲开启可重复读事务,读取五千行后保持事务打开: ...

六月 4, 2027 · 1 分钟 · XB Field Notes