作者简介

Laurenz Albe cybertec公司工程师

译者简介

王志斌,从事数据库产品相关工作,主要致力于postgresql数据库高可用解决方案及云端产品化工作。

校对者简介

崔鹏,PostgreSQL爱好者。海能达PostgreSQL高级DBA。

为什么是VACUUM?

每当更新或删除PostgreSQL表中的行时,都会留下死元组。VACUUM摆脱了它们,以便空间可以重复使用。如果一个表没有被清理,它就会变得臃肿,这会浪费磁盘空间并减慢表的顺序扫描(以及索引扫描)。

VACUUM还负责冻结表行,以避免在事务ID计数器环绕时出现问题,但这是另一回事。

通常,您不必处理所有这些,因为PostgreSQL中内置的autovacuum 守护程序会为您完成这些工作。

问题

如果你的表变得臃肿,首先你需要检查的是 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是0并且last_autovacuum是 NULL,你可能有统计信息收集器的问题。

如果臃肿的表就在顶部,但last_autovacuum为NULL,则您可能需要将autovacuum 配置为更积极,以便完成对表的处理。

但有时结果会如下所示:

schemaname |    relname   | n_live_tup | n_dead_tup |   last_autovacuum
------------+--------------+------------+------------+---------------------
 laurenz    | vacme        |      50000 |      50000 | 2018-02-22 13:20:16
 pg_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 |
(10 rows)

autovacuum最近在这里运行,但它没有释放死元组!

我们可以通过运行VACUUM (VERBOSE)来验证问:

test=> VACUUM (VERBOSE) vacme;
INFO:  vacuuming "laurenz.vacme"
INFO:  "vacme": found 0 removable, 100000 nonremovable row versions in
       443 out of 443 pages
DETAIL:  50000 dead row versions cannot be removed yet,
         oldest xmin: 22300
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.01 s, system: 0.00 s, elapsed: 0.01 s.

为什么VACUUM不能删除死元组?

VACUUM只能删除不再需要的那些行版本(也称为“元组”)。如果删除事务的事务 ID(存储在xmax系统列中)比PostgreSQL数据库(或共享表的整个集群)中仍处于活动状态的最旧事务更旧,则不清理元组。

这个值(VACUUM上面输出中的 22300)称为“xmin 水平”。

在 PostgreSQL集群中,有三件事可以阻止这个xmin范围:

长时间运行的事务:

您可以使用以下查询找到它们及xmin的值:

SELECT pid, datname, usename, state, backend_xmin
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL
ORDER BY age(backend_xmin) DESC;

可以使用 pg_terminate_backend() 函数来终止阻塞VACUUM的数据库会话。

废弃的复制槽:

复制槽是一种数据结构,可防止PostgreSQL服务器丢弃备用服务器仍需要的信息以赶上主服务器。

如果复制延迟或备用服务器关闭,复制槽将阻止VACUUM 删除旧行。

您可以使用以下查询找到所有复制槽及其 xmin 值:

SELECT slot_name, slot_type, database, xmin
FROM pg_replication_slots
ORDER BY age(xmin) DESC;

使用 pg_drop_replication_slot() 函数删除不再需要的复制槽。

注意:如果 hot_standby_feedback = on,这只会发生在物理复制中。对于逻辑复制,也有类似的危险,但只有系统目录受到影响。在这种情况下检查列 catalog_xmin。

孤立的准备事务:

在两阶段提交期间,首先使用PREPARE语句准备分布式事务,然后使用 COMMIT PREPARED语句提交。

一旦准备好事务,它就会一直“徘徊”,直到它被提交或中止。它甚至必须在服务器重启后幸存下来!通常,事务不会长时间保持准备状态,但有时会出现问题,管理员必须手动删除准备好的事务。

您可以使用以下查询找到所有准备好的交易及其 xmin 值:

SELECT gid, prepared, owner, database, transaction AS xmin

FROM pg_prepared_xacts

ORDER BY age(transaction) DESC;

使用ROLLBACK PREPAREDSQL语句删除准备好的事务。

PostgreSQL中文社区欢迎广大技术人员投稿

投稿邮箱:press@postgres.cn

请在登录后发表评论,否则无法保存。
1楼 Kenny
2023-03-08 16:46:23+08

What's out?

2楼 cg123456
2022-06-11 11:15:18+08

111

© 2010 PostgreSQL中文社区