文章目录
- 1.需要执行vacuum的原因
- 2.VACUUM存在的问题:表膨胀
- 3.查找原因
- 4.阻止vacuum清理死行的四个原因
- 4.1 长事务:
- 4.2 弃用的replication slot和VACUUM:
- 4.3 孤立的prepared transactions:
- 4.4 启用 hot_standby_feedback 的备用服务器(standby server)
1.需要执行vacuum的原因
每当update或delete表中的行时,都会留下死行(dead rows)。 VACUUM 将会负责清除它们,以便可以重复使用空间。 如果表没有被清理,它将变得臃肿,这会浪费磁盘空间并减慢全表扫描(以及在较小程度上 - 索引扫描)。
VACUUM 还负责冻结(freezing)表行,以避免transaction ID 计数器回绕时出现问题。
通常不必关心这些,因为 PostgreSQL 中内置的 autovacuum 守护进程会自动完成这些工作。 要了解有关启用和禁用autovacumm的信息,请阅读PG在线文档。
2.VACUUM存在的问题:表膨胀
如果表膨胀,首先要检查的是 autovacuum 是否处理了它们:
SELECT schemaname, relname, n_live_tup, n_dead_tup, last_autovacuum
FROM pg_stat_all_tables
ORDER BY n_dead_tup/ (n_live_tup* current_setting('autovacuum_vacuum_scale_factor')::float8+ current_setting('autovacuum_vacuum_threshold')::float8)DESC
LIMIT 10;
如果膨胀的表没有显示在此处,n_dead_tup 为零且last_autovacuum 为NULL,则统计收集可能存在问题。
如果膨胀的表位于顶部,但 last_autovacuum 为 NULL,则可能需要将 autovacuum 配置为更积极。
但有时结果会是这样:
schemaname | relname | n_live_tup | n_dead_tup | last_autovacuum
------------+--------------+------------+------------+---------------------public | t1 | 500 | 500 | 2024-03-13 11:05:18pg_catalog | pg_attribute | 42 | 165 |pg_catalog | pg_amop | 871 | 162 |pg_catalog | pg_class | 9 | 31 |pg_catalog | pg_type | 17 | 27 |pg_catalog | pg_index | 5 | 15 |pg_catalog | pg_depend | 9162 | 471 |pg_catalog | pg_trigger | 0 | 12 |pg_catalog | pg_proc | 183 | 16 |pg_catalog | pg_shdepend | 7 | 6 |
以上显示最近运行了 autovacuum,但它却没有释放死元组!
3.查找原因
可以通过运行 VACUUM (VERBOSE) 来验证问题:
vacuum (verbose) public.t1
输出如下:
INFO: vacuuming “public.t1”
INFO: “t1”: found 0 removable, 1000 nonremovable row versions in 5 out of 5 pages
DETAIL: 500 dead row versions cannot be removed yet, oldest xmin: 33783784
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
VACUUM
Query returned successfully in 78 msec.
为什么 VACUUM 未能删除死行?
因为VACUUM 只会移除那些“不再需要”的行版本(也称为“元组”)。如果删除事务的事务ID(存储在 xmax 系统列中)比 PostgreSQL 数据库中仍然活跃的最老事务的事务 ID(或者对于共享表而言,整个集群中的最老事务)要旧,那么该元组就被视为“不再需要”。
在上面的 VACUUM 输出中,这个值(33783784)称为“xmin horizon”。
4.阻止vacuum清理死行的四个原因
在 PostgreSQL 集群中,有三件事会阻碍这个 xmin 范围:
4.1 长事务:
SELECT pid, datname, usename, state, backend_xmin, backend_xid
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL OR backend_xid IS NOT NULL
ORDER BY greatest(age(backend_xmin), age(backend_xid)) DESC;
可以使用 pg_terminate_backend() 函数来终止阻止 VACUUM 的数据库会话。
pg_terminate_backend()
4.2 弃用的replication slot和VACUUM:
application slot是一种数据结构,它可以防止 PostgreSQL 服务器丢弃仍然需要的信息,这些信息对于备用服务器来说是必要的,以便跟上主服务器的步伐。
可以使用以下查询找到所有application slot及其 xmin 值:
SELECT slot_name, slot_type, database, xmin
FROM pg_replication_slots
ORDER BY age(xmin) DESC;
使用 pg_drop_replication_slot() 函数删除不再需要的application slot。
pg_drop_replication_slot()
注意:只有当 hot_standby_feedback = on 时,才会在物理复制中发生这种情况。 对于逻辑复制也存在类似的危险,但它只影响系统catalog。 在这种情况下,请检查 Catalog_xmin 列。
4.3 孤立的prepared transactions:
在两阶段提交期间,首先使用 PREPARE 语句准备分布式事务,然后使用 COMMIT PREPARED 语句提交。
一旦 Postgres 准备好事务,该事务就会保持“挂起”状态,直到 Postgres 提交或中止它。 它甚至必须在服务器重新启动后继续存在! 通常,事务不会长时间保持"prepared"状态,但有时会出现问题,管理员必须手动删除"prepared"的事务。
可以使用以下查询找到所有准备好的交易及其 xmin 值:
SELECT gid, prepared, owner, database, transaction AS xmin
FROM pg_prepared_xacts
ORDER BY age(transaction) DESC;
使用 ROLLBACK PREPARED SQL 语句删除准备好的事务。
ROLLBACK PREPARED
4.4 启用 hot_standby_feedback 的备用服务器(standby server)
通常,流复制设置中的主服务器不关心备用服务器上运行的查询。 因此,VACUUM 会很高兴地删除死行,备用数据库上长时间运行的查询可能仍然需要这些死行,这可能会导致复制冲突。 为了减少复制冲突,可以在备用服务器上设置hot_standby_feedback = on。 然后,备用数据库将向主数据库通报最旧的打开事务,并且主数据库上的 VACUUM 不会删除备用数据库上仍需要的旧行版本。
要找出所有备用服务器的 xmin,您可以在主服务器上运行以下查询:
SELECT application_name, client_addr, backend_xmin
FROM pg_stat_replication
ORDER BY age(backend_xmin) DESC;