PostgreSQL vacuum freeze问题

703 阅读2分钟

起因

PostgreSQL的tuple头信息中有两个字段xmin、xmax分别用于标记行产生于变更的事务号,以标识记录的版本好,事务的可见性等。事务号的长度是32bit,因此PG设计了事务存活的最长时间是20亿,如果事务年龄超过20亿,则将这个事务置为frozen。 昨天有个PostgreSQL9.6.19的数据库年龄达到了21亿,数据库拒绝执行任何操作。开始提示类似如下的报错:

WARNING: database “postgres” must be vacuumed within XXX transactions. 
HINT: To avoid a database shutdown, execute a database-wide VACUUM in that database. You might also need to commit or roll back old prepared transactions.

处理

通常的处理方式是停止数据库,进入单用户模式,然后执行vacuum freeze或vacuum。但失败了vacuum操作了运行了一段时间后,进程就进入了休眠状态,好像是在等资源释放。因为是生产系统的数据库,没有进一步排查等待的资源。把数据库做了个备份,做了如下处理。 启动数据库,查询了如下语句:

SELECT * FROM pg_replication_slots;
SELECT * FROM pg_prepared_xacts;
SELECT oid, datname, datfrozenxid, age(datfrozenxid), datminmxid, mxid_age(datminmxid) FROM pg_database ORDER BY age(datfrozenxid) DESC;
SELECT * FROM pg_control_checkpoint();
SELECT oid, oid::regclass, relkind, relfrozenxid, age(relfrozenxid) FROM pg_class WHERE relfrozenxid <> 0 AND age(relfrozenxid) > 1800000000 ORDER BY age(relfrozenxid) DESC;

确定了没有复制槽,没有两阶段提交事务,生产系统使用的数据库年龄最大,已超过20亿,找出了年龄超过18亿的9张表。 没有其他什么太特殊的原因,决定从这9张表入手,分别查询了9张表的大小,最大的数据量在百万级别,其他表的数据量从数千到十几万,都不是很大,但表年龄很大,dead tuple很多。 有了备份操作也大胆了很多,进入单用户模式,单独对表进行vacuum 操作,没有反应,与全库做vacuum操作一样,运行一段时间后进入休眠。加verbose,再执行,同样情况,且不打印任何信息。最后选了一个小表尝试了vacuum full verbose操作,打印了详细信息,且成功将表的年龄降下来了。其他表依次进行操作,最后对整库进行vacuum操作,数据库年龄成功降低。

总结

1、上面操作有一定危险性,请在做好备份,评估风险的情况下进行操作。 2、vacuum等待原因比较多,复制槽、长事务、二阶段提交、数据表损坏都有可能造成等待。本次遇到的可能是表损坏。 3、本次数据库出问题是autovacuum进程关闭了半年,且中间没有人员关注及维护。最终造成数据库出现该问题。