PostgreSQL 数据库日志出现 checkpoints are occurring too frequently
PostgreSQL 数据库日志出现 checkpoints are occurring too frequently
1. 背景
最近有同事反馈在项目压测环境发现数据库服务器的 IO 一直特别高,需要上去排查分析下问题。
于是我立马上服务器上通过 iotop 命令查看进程的读写情况,但是却发现 PostgreSQL 的进程都是 IDLE 状态,但是很奇怪的是, IDLE 状态的进程还一直在写磁盘。于是想着去日志里面看看是不是有什么线索,果不其然在日志中看到如下内容:
2024-04-28 10:08:07.618 CST [1647] LOG: checkpoints are occurring too frequently (25 seconds apart)
2024-04-28 10:08:07.618 CST [1647] HINT: Consider increasing the configuration parameter "max_wal_size".
2024-04-28 10:09:07.314 CST [1647] LOG: checkpoints are occurring too frequently (29 seconds apart)
2024-04-28 10:09:07.314 CST [1647] HINT: Consider increasing the configuration parameter "max_wal_size".
2024-04-28 10:10:05.923 CST [1647] LOG: checkpoints are occurring too frequently (28 seconds apart)
2024-04-28 10:10:05.923 CST [1647] HINT: Consider increasing the configuration parameter "max_wal_size".
2024-04-28 10:11:05.184 CST [1647] LOG: checkpoints are occurring too frequently (29 seconds apart)
2024-04-28 10:11:05.184 CST [1647] HINT: Consider increasing the configuration parameter "max_wal_size".
2024-04-28 10:11:34.618 CST [1647] LOG: checkpoints are occurring too frequently (29 seconds apart)
2024-04-28 10:11:34.618 CST [1647] HINT: Consider increasing the configuration parameter "max_wal_size".
2024-04-28 10:12:02.580 CST [1647] LOG: checkpoints are occurring too frequently (28 seconds apart)
2024-04-28 10:12:02.580 CST [1647] HINT: Consider increasing the configuration parameter "max_wal_size".
2024-04-28 10:12:31.648 CST [1647] LOG: checkpoints are occurring too frequently (29 seconds apart)
2024-04-28 10:12:31.648 CST [1647] HINT: Consider increasing the configuration parameter "max_wal_size".
2. 分析
从上面的日志中,可以得到的几点问题:
检查点频繁发生,日志中多次检查点 (
checkpoints
) 发生的间隔非常短(大约 25 到 29 秒)。在 PostgreSQL 中,检查点是数据库定期将内存中的数据( 也就是我们俗称的 脏页 )和 事务日志同步到磁盘上的过程。数据库提示我们增加 max_wal_size 这个参数的值。
HINT: Consider increasing the configuration parameter “max_wal_size”.
我们的应用是一个物联网设备相关的项目,设备会定时上报数据,然后系统会高频繁地对数据库进行插入更新的操作。 根据上面的提示信息第一时间猜测是数据库参数可能配置的不太对,导致检查点被频繁触发。查看数据库 postgresql.conf 中的配置如下:
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
# fdatasync (default on Linux and FreeBSD)
# fsync
# fsync_writethrough
# open_sync
#full_page_writes = on # recover from partial page writes
#wal_compression = off # enable compression of full-page writes
#wal_log_hints = off # also do full page writes of non-critical updates
# (change requires restart)
#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers # WAL 日志的缓冲区大小
# (change requires restart)
#wal_writer_delay = 200ms # 1-10000 milliseconds
#wal_writer_flush_after = 1MB # measured in pages, 0 disables
#commit_delay = 0 # range 0-100000, in microseconds
#commit_siblings = 5 # range 1-1000
# - Checkpoints -
#checkpoint_timeout = 5min # range 30s-1d
max_wal_size = 1GB # 最小的WAL大小
min_wal_size = 80MB # 最大的WAL大小
#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
#checkpoint_flush_after = 256kB # measured in pages, 0 disables
#checkpoint_warning = 30s # 0 disables
可以看到 wal 相关的参数大部分都是用的系统默认的,只有 max_wal_size 、以及 min_wal_size 两个值是显示指定了参数值。 对于我们这种高频批量插入以及批量更新数据的业务场景,我们可以尝试对参数进行如下调整:
max_wal_size
参数调整成 8 GB 或更高, 这个参数限制了触发检查点之前可以积累的 WAL 数据的最大量。如果 WAL 增长到这个尺寸,就会触发检查点。我们的业务场景中,很快就可以达到默认的最大阈值,pg_wal目录的大小会急剧增加。 增加此值可以减少检查点的频率,从而提高系统性能。这里需要注意的是,这会增加数据库崩溃后恢复的时间。checkpoint_completion_target
参数调整成 0.9 ,从配置文件中可以看出默认是 0.5 ,这意味着 PostgreSQL 会尝试在两次checkpoint_timeout
时间段的一半时间内完成写入磁盘的操作。 在我们这种业务场景下,设置成 0.5 这个值太小了,数据库就会快速写入磁盘,会影响正常的业务功能执行。这个值可以设置成 0.7 - 0.9 。这个值调整后的问题是如果设置的太高,可以减少检查点期间的 IO 峰值,但是在检查点完成前如果发生故障,未同步的数据会更多,可能会影响数据的安全。wal_compression
这个参数我尝试调整成 on ,但是效果不太明显。
修改完参数后,重启系统,观察 Grafana 中的监控可以明显看到 IO 降下去。
3. 术语解释
3.1 WAL
WAL 是 Write-Ahead Logging 的缩写,在修改数据库内容之前,先将这些修改记录到一个日志中。在关系型数据库中,基本都有它的身影。
通过这种机制可以确保即使在发生故障的情况下,所有已提交的事务也都可以从这些日志中恢复。
WAL 的流程大致如下:
开始
|
V---> 事务执行,开始写入数据
|
V---> WAL (Write-Ahead Logging) 文件增长
| |
| |---> WAL 文件大小是否超过 max_wal_size?
| | |
| | No | Yes
| V V
| 继续写入 触发检查点
| (Checkpoint)
| |
| V
| 检查点是否由 max_wal_size 触发?
| | No | Yes
| V V
| 继续操作 检查点完成后,WAL 文件大小减少
| |
| V
| checkpoint_timeout 是否到达?
| | No | Yes
| V V
| 继续操作 触发定时检查点
| (由 timeout 控制)
| |
| V
| 检查点完成后,WAL 文件大小减少
|
V---> 事务持续进行,重复上述过程
|
结束
3.2 CheckPoint 检查点
在 PostgreSQL 数据库系统中,检查点就像是给数据库的数据做一个“快照”,保存当前的状态。当数据库运行时,很多数据最初只是暂时存储在内存中,这让数据处理得更快。但如果这时候电脑突然断电或者出现故障,那么内存中的数据就会丢失。为了防止这种情况,数据库会定期地把内存中的数据写入到硬盘上,这个过程就是一个检查点。
检查点可以由以下几种情况触发:
1. 时间间隔:由 checkpoint_timeout
参数控制,这是两个检查点之间的最大时间间隔。
2. WAL 文件大小:由 max_wal_size
参数控制,当 WAL 文件增长到一定大小时,触发检查点以避免 WAL 文件过大。
3. 服务器关闭:当数据库服务器正常关闭时,会自动执行检查点操作,以确保所有数据都安全地写入硬盘。
4. 手动触发:管理员可以手动触发检查点,例如通过 SQL 命令 CHECKPOINT;
。
在检查点期间,系统会执行以下步骤:
- 标记检查点开始:系统记录当前的事务日志位置,这标志着检查点的开始。
- 刷新脏页:将所有修改过但尚未写入硬盘的数据页(脏页)写入到硬盘中。这确保了数据的持久性。
- 日志切割:完成数据刷新后,系统可能会执行 WAL 日志的切割,将旧的 WAL 文件归档或删除,释放空间供新的事务日志使用。
- 标记检查点完成:记录检查点完成的日志位置,这意味着所有之前的数据变更都已经安全地记录在硬盘上。
合理配置 max_wal_size
和 checkpoint_timeout
参数,以平衡系统性能和数据安全性是一个挑战。
4. 参考链接
- http://www.postgres.cn/docs/9.3/wal-configuration.html PostgreSQL 可靠性和预写式日志
- https://www.postgresql.org/docs/9.3/wal-intro.html PostgreSQL Write-Ahead Logging (WAL)
- https://www.cybertec-postgresql.com/en/hot-updates-in-postgresql-for-better-performance ] (https://www.cybertec-postgresql.com/en/hot-updates-in-postgresql-for-better-performance/) HOT UPDATES IN POSTGRESQL FOR BETTER PERFORMANCE