PostgreSQL故障排查 -- 磁盘写满, autovacuum不香了

PostgreSQL运维中,遇到一两个没预期到故障,应该不是难事:(,比如这两天就刚解决掉一个磁盘无故写爆的案例。

autovacuum

PostgreSQL实现MVCC的机制需要有一个vacuum的过程,为了减弱人工介入的必要性,在PG Server启动时,默认会自动拉起autovacuum。

有了autovacuum并不意味着可以高枕无忧, 在某些情况下,autovacuum无法清除dead tuples,磁盘无法有效清理,最终写爆。

可能一 长时间的Query

如果某些Query运行了很长时间,比如几个小时甚至几天,那么这些Query就会导致autovacuum无法清除垃圾数据。

假设这些查询是在T0时刻启动,在T1时刻,有数据被删除或者更新,在T2时刻,autovacuum开始进行清理,在这个新启动的清除过程中,只有T0时刻之前的dead tuples会被有效清理,而在T1时刻被删除的数据无法被清除,因为T0时刻的查询依然需要这些数据。

要解决这个问题,就必须终止长时间的query。一种是等查询自然完成,另一种是显式的kill, 假设某query的pid是123, 那么使用 pg_terminate_backendpg_cancel_backend

select pg_terminate_backend(query_pid)

可能二 废弃的replication slots

如果某些subscriptor订阅了CDC消息,但是subscriber异常退出后,并没有删除对应的replication slots,那么由于数据没有被消费,所以dead tuples也会被一直保留。

pg_drop_replication_slot() 删除废弃不用的复制槽位。

可能三 僵死或处于孤儿状态的 prepared transaction

在两阶段提交(two-phase commit)中, 需要在第一步创建prepared transaction, 如果因为某种原因prepared transaction一直没有结束,那么从创建这个预事务之后的所有dead tuples无法得到清除。

处理办法,先利用视图 pg_prepared_xacts 列出有哪些僵死的预事务,然后用 rollback prepared transaction_id 来回退该事务。

rollback prepared 链接有详细的示例说明