一、简介
数据库使用 32 位事务号,最大容纳 42 亿左右的事务号,事务号是循环使用的。当前事务号过去的 21 亿事务属于过去的事务号,当前事务号往前的 21亿 属于未来的事务号,未来的事务号对当前事务是不可见的。当事务号处于未来事务时,就会导致该事务不可见(事务 ID 回卷),为了预防此类情况发生,数据库事务号在达到阈值时 autovacuum 会自动冻结(freeze)重置事务 ID 为 2(冻结的事务id,比任何普通的事务 ID 都旧),来防止事务 ID 回卷。出于各种原因,数据库无法自动从一个表中清除旧的事务 ID,当数据库的最旧事务 ID 和回卷点之间达到一千一百万个事务时,数据库将开始发出这样的警告消息:
WARNING: database "mydb" must be vacuumed within 10985967 transactions
HINT: To avoid a database shutdown, execute a database-wide VACUUM in that database.
如提示信息所建议,一次手动的 VACUUM 应该会修复该问题;但是注意 VACUUM 必须由一个超级用户来执行,否则它将无法处理系统目录并且不能推进数据库的 datfrozenxid。如果这些警告被忽略,一旦距离回卷点只剩下一百万个事务时,数据库将会关闭并且拒绝开始任何新的事务:
ERROR: database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT: Stop the postmaster and vacuum that database in single-user mode.
这一百万个事务的富余是为了让管理员能通过手动执行所要求的 VACUUM 命令进行恢复而不丢失数据。但一旦数据库进入到安全关闭模式,唯一方法是停止数据库,以单一用户启动数据库来执行 VACUUM。
二、清理年龄操作示例
数据库正常运行时:
--查询所有数据库的年龄,定位年龄超限的数据库
SELECT datname, age(datfrozenxid) FROM pg_database;--对年龄超限的数据库,查询库下所有表的年龄,找到年龄超限的对象
SELECT oid, relname, relfrozenxid, age(relfrozenxid) FROM pg_class where relfrozenxid != 0 order by age(relfrozenxid) desc limit 20; --对年龄超限的对象执行清理操作
VACUUM objectname;
或
VACUUM FREEZE objectname;
VACUUM 为懒惰模式清理对象年龄,只会扫描包含死元组的页面。VACUUM FREEZE 为激进模式清理对象年龄,会扫描所有页面。
数据库进入到安全关闭模式时:
--停止数据库
pg_ctl stop--单用户模式连入对应数据库
postgres --single -D $PGDATA databasename--执行清理操作
VACUUM FREEZE VERBOSE;