PostgreSQL WAL 文件数量长期持续增加问题排查

1. 提出问题

最近遇到一个自己运维 DB 的开发同学反馈:"我们的业务量较小,但现在数据库积累了大量的 WAL, 一个月的时间 pg_wal 目录和归档的目录均超过了80GB, 且 WAL 增长的速度较快,由于虚拟机的磁盘不大,几乎把磁盘撑爆了,我应该怎么处理呢?可以先人工删一下吗?”

针对这个现象,我们需要分析如下几个问题:

  1. 几乎没有业务为什么 WAL 还会增加且增加的还很快?

  2. WAL 会一直增加直至把磁盘撑爆吗?DBMS 有参数控制上限么?

  3. WAL 文件为什么会保留那么多, DBMS 难道不会自动清理, 需要人工介入么?

2. 分析问题

因为 PostgreSQL 不同版本之间参数有差异,所以确认运行环境是排查问题的第一步。

经确认, 出现这一现象的 DB 版本是 PostgreSQL 11,那么针对 pg_wal 目录下文件数量和尺寸的问题,我们首先明确如下几个相关参数的含义:

  • wal_segment_size:单个 WAL 文件的大小,默认为16MB,一般不做更改,且在 pg11 之前,只能在编译 pg 时指定,很明显这个参数的影响可以排除。

  • wal_keep_segments:这个参数配置的是 standby 复制所需的在 pg_wal 目录中最少保留的 WAL 文件的数目,一般情况下,这大概就是 wal 尺寸范围,如果这个参数配置为0,那么此时 WAL 文件的数量还取决于如下其他参数。

  • min_wal_size:这个是 WAL 文件保留的最小尺寸,当 WAL 的使用低于这个配置,那么 WAL 将会在 checkpoint 时候对 WAL 以 recycle 的形式处理,而不是直接 remove。当然这个参数也是有配置范围限制的:min_wal_size 至少是 wal_segment_size 的2倍。

  • max_wal_size:这个参数指定是 checkpoint 和 checkpoint 之间产生的 WAL 最大量。当然这只是一个软限制,在集群负载高或者一些其他异常情况下可能会超过这个值。结合 checkpoint_completion_target 参数, 当新产生的 wal 文件到达特定的数据量后便会触发 checkpoint。

  • archive_timeout:如果 DB 实例写入量很少,到达 archive_timeout 会触发 WAL 文件的强制切换。因此如果 archive_timeout 如果太短就会产生很多新的 WAL,从而产生大量归档。

除了这些参数配置外,还有一些异常情况会造成 WAL 堆积:

  • 如果开启了归档,那么归档命令成功了,WAL 才会被 remove/recycle,所以这里需要注意,如果归档命令是失效的,那么 pg_wal 目录会一直增长,不会自动删除 WAL,此目录会持续增长,进而有撑爆磁盘的风险。

  • 在创建了复制槽后,如果通过复制槽不能正常消费掉,则会造成 WAL 堆积,导致主库的 WAL 文件数量一直增加,pg_wal 目录也会一直增长。

3. 解决问题

根据上述参数描述和分析,我们首先检查 DB 实例的上述参数。

postgres=# select version();                                                 version                                                --------------------------------------------------------------------------------------------------------- PostgreSQL 11.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16), 64-bit  postgres=# show wal_keep_segments ; wal_keep_segments------------------- 128(1 row) postgres=# show min_wal_size ; min_wal_size-------------- 80MB(1 row) postgres=# show max_wal_size ; max_wal_size-------------- 5GB(1 row) postgres=# show archive_timeout; archive_timeout----------------- 1min(1 row)  postgres=# select * from pg_stat_get_archiver();-[ RECORD 1 ]------+------------------------------archived_count     | 6053last_archived_wal  | 00000001 00000017 000000A5last_archived_time | 2020-09-30 11:51:04.366339+08failed_count       | 0last_failed_wal    |last_failed_time   |stats_reset        | 2020-08-14 15:02:23.166376+08

可见,wal_keep_segments 值为128,但 archive_timeout 设置较小,这就回答了“几乎没有业务,为什么 WAL 还会增加且增加的很快”这个问题。建议调大 archive_timeout 参数来降低 WAL 文件的强制切换频率,WAL 产生过快的问题得以优化。

pg_wal 目录的文件轻易不要人工处理,但归档目录需要定期将历史 WAL 备份走然后删掉,在磁盘紧张的情况下可以先将归档这部分处理掉。

继续观察,由于 failed_count 是0, 且同时检查 DB log 没有任何异常信息,可以排除归档失败导致的 pg_wal 目录一直增加。

通过上述信息及分析可断定 WAL 堆积不是由于 wal_keep_segments 参数配置过大或归档失败导致,接下来我们重点分析下 pg_replication_slots。

postgres=# select * from pg_replication_slots ;-[ RECORD 1 ]-------+-----------slot_name           | pgstandby1plugin              |slot_type           | physicaldatoid              |database            |temporary           | factive              | factive_pid          |xmin                |catalog_xmin        |restart_lsn         | 0/B000000confirmed_flush_lsn |

首先这个复制槽的激活状态"active = f",可以确定其不是被某些进程正在使用的此复制槽。

接下来我们查看 restart_lsn 对应的 WAL 文件名。

postgres=# select pg_walfile_name('0/B000000');-[ RECORD 1 ]---+-------------------------pg_walfile_name | 00000001000000000000000A

从 pg_wal 目录中可以看到,这个文件是最老的 WAL 文件,创建时间是 2020-08-14,

[postgres@dbhost /export/pgdata/pg_wal]$ ll 00000001000000000000000A-rw------- 1 postgres postgres 16777216 Aug 14 15:03 00000001000000000000000A

所以从这里我们基本上可以判断 WAL 保留这么多是物理槽占用导致的。

此外,我们继续确认下当前 wal_lsn 和上面开始堆积的 lsn 差异是不是和现在的 pg_wal 尺寸吻合呢?

postgres=# select pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),'0/B000000')); pg_size_pretty---------------- 94 GB(1 row)


这里的总尺寸也对上了!

问题原因找到了,那么接下来就可以对症下药。首先确认下这个复制槽是干什么用的,如果这个复制槽有用,那么把这些堆积的 WAL 消费掉就可以了,如果这个复制槽没啥用,那么把可以直接 drop 掉这个物理复制槽。

postgres=# select pg_drop_replication_slot('pgstandby1'); pg_drop_replication_slot-------------------------- (1 row)

正常情况下,数据库在做 checkpoint 时或在数据库重启时会随着 recovery 的 checkpoint 而自动 remove/recycle。那么接下来,我们选择手动执行一个 checkpoint 或等待下一个系统自动的 checkpoint 后 pg_wal 目录的尺寸就会降下来。

postgres=# checkpoint;CHECKPOINTpostgres=# \q [postgres@dbhost /export/pgdata/pg_wal]$ du -sh2G    .

4. 结论

综述,本案例情形已经明朗:

  • 几乎没有业务的数据库 WAL 量也会增加是因为周期性强制切换 WAL,如果想要 WAL 切换速度降低,我们可以适当调大 archive_timeout,比如从1min 增大为10min 甚至更长时间

  • DBMS 有参数控制 WAL 的增长幅度和总尺寸,但是很多都是软限制,而非强制。出现异常的情况下,pg_wal 确实是会无限膨胀下去将磁盘撑爆,DBMS 无法自动处理,需要人工介入。

  • 产生 WAL 堆积在排除参数配置且归档成功的情况下,是因为物理复制槽占用导致。可见在此极端情况下,WAL 文件确实会一直堆积,进而出现占满磁盘的情况,导致 DB 实例异常 shutdown 而产生故障。

DBMS 正常情况下是可以对 WAL 进行自动 remove/recycle,如果出现 WAL 清理不掉的情况,且参数无可疑之处,多半是其他原因引起的,需要仔细分析原因,切忌“头痛医头脚痛医脚”。比如此例中是因为废弃的物理复制槽导致,其实废弃的逻辑复制槽亦可导致此问题。

监控是 DBA 的眼睛,为了防止这样的意外发生,DBA 可以配置对 pg_wal 总尺寸大小进行监控, 同时也可以对 pg_replication_slots 的状态进行监控,双管齐下,防患于未然。


上一篇:CSS的常用选择器总结


下一篇:语音降噪-维纳滤波