我们常说的数据库中WAL是什么
【摘要】 一、先搞懂:WAL 是什么?为什么不能删?WAL 是 PostgreSQL 的 “事务日志”,所有写操作(INSERT/UPDATE/DELETE)都会先写入 WAL,再写入数据文件(data file),核心作用有 3 个:崩溃恢复:数据库意外宕机时,重启后通过 WAL 回放未完成的事务,确保数据一致性(不会丢数据);主从复制:主库的 WAL 日志会同步到备库,备库通过回放 WAL 实现数...
一、先搞懂:WAL 是什么?为什么不能删?
WAL 是 PostgreSQL 的 “事务日志”,所有写操作(INSERT/UPDATE/DELETE)都会先写入 WAL,再写入数据文件(data file),核心作用有 3 个:
- 崩溃恢复:数据库意外宕机时,重启后通过 WAL 回放未完成的事务,确保数据一致性(不会丢数据);
- 主从复制:主库的 WAL 日志会同步到备库,备库通过回放 WAL 实现数据同步(备库没同步完,主库不会删对应 WAL);
- 时间点恢复(PITR):如果开启了归档备份(比如把 WAL 归档到 S3 / 本地),可通过 “基础备份 + WAL 归档” 恢复到任意时间点。
PostgreSQL 默认会自动清理过期的 WAL 文件(已同步到备库、已归档、超过保留策略的文件),pg_wal 暴涨说明 “自动清理” 被卡住了,而非 WAL 本身是 “垃圾”。
二、pg_wal 暴涨的 5 个常见原因(对应排查步骤)
结合你的场景(运行半年、数据没增加),WAL 堆积大概率是以下原因,按优先级排查:
1. 主从复制延迟 / 备库异常(最常见)
如果数据库是主从架构,主库会保留备库未同步的 WAL 文件。如果备库宕机、网络中断、同步速度跟不上,主库会一直保留这些 WAL,直到备库同步完成或超过
wal_keep_size限制。排查命令(主库执行):
-- 查看备库同步状态
SELECT
client_addr AS 备库IP,
state AS 同步状态,
sent_lsn, write_lsn, flush_lsn, replay_lsn, -- LSN是WAL日志的位置标记
now() - write_time AS 写入延迟,
now() - replay_time AS 回放延迟
FROM pg_stat_replication;
- 如果
state不是streaming(比如down):备库异常,需重启备库或修复网络; - 如果
replay_lsn < sent_lsn:备库有同步延迟,需检查备库资源(CPU/IO 是否满)、是否有慢查询阻塞回放。
2. 归档备份失败(开启了 WAL 归档但没成功)
如果配置了
archive_command(将 WAL 归档到备份存储),但归档命令执行失败(比如备份存储满、权限不足、网络问题),PostgreSQL 会认为 “WAL 还没归档完成”,不会清理,导致堆积。排查命令:
-- 查看归档状态
SELECT
archiver, -- 归档进程状态(running=正常,failed=失败)
last_archived_wal, -- 最后一次成功归档的WAL文件
last_archived_time, -- 最后归档时间
failed_count, -- 归档失败次数
last_failed_wal, -- 最后失败的WAL文件
last_failed_time -- 最后失败时间
FROM pg_stat_archiver;
若archiver=failed或failed_count>0:需修复archive_command(比如检查备份路径权限、存储是否可用)。
3. 长事务 / 空闲事务阻塞
PostgreSQL 的 WAL 清理依赖 “事务 ID 推进”,如果存在长时间未结束的事务(比如运行几小时的查询)或空闲事务(连接没关闭,事务没提交 / 回滚),会导致旧的 WAL 文件无法回收(因为要保证事务能看到一致性快照)。
-- 查看运行超过1小时的事务(重点看idle in transaction状态)
SELECT
pid,
usename,
datname,
state,
wait_event,
now() - query_start AS 运行时长,
query
FROM pg_stat_activity
WHERE
(state = 'idle in transaction' OR state = 'active')
AND now() - query_start > '1 hour'::interval
ORDER BY 运行时长 DESC;
若有长时间运行的事务:需评估是否可以终止(SELECT pg_terminate_backend(pid);),避免阻塞 WAL 回收。
4. 配置参数不当
关键参数设置不合理,会导致 WAL “过度保留”:
wal_keep_size:主库额外保留的 WAL 大小(默认 0),若设太大(比如 10GB),即使备库同步完成,主库仍会保留该大小的 WAL;max_wal_size:WAL 段的最大总大小(默认 16GB),超过会触发检查点(Checkpoint),但如果归档 / 同步卡住,这个参数也无法限制堆积;min_wal_size:WAL 的最小保留大小(默认 80MB),影响检查点触发后的 WAL 回收。
5. 手动备份未正常结束
如果执行过
pg_start_backup()手动备份,但没执行pg_stop_backup(),PostgreSQL 会认为 “备份还在进行”,一直保留备份期间产生的所有 WAL,导致暴涨。-- 查看是否有未结束的备份
SELECT * FROM pg_stat_backup;
- 若有未结束的备份:需执行
pg_stop_backup()终止(如果备份已完成)。
三、解决步骤:先紧急清理(避免磁盘满),再长期控制
第一步:紧急清理(线上优先操作)
目标是快速回收 WAL 空间,避免磁盘耗尽,需先排查并解决上述 “清理阻塞原因”:
1. 先解决阻塞因素(关键!否则清理后还会堆积)
- 若备库异常:重启备库、修复网络,等待
replay_lsn追上sent_lsn(主从同步完成); - 若归档失败:修复
archive_command(比如更换归档路径、清理备份存储空间),执行SELECT pg_switch_wal();触发 WAL 切换,让归档进程重新尝试归档; - 若有长事务:终止长时间运行的空闲事务或慢查询(
pg_terminate_backend(pid)); - 若备份未结束:执行
pg_stop_backup()。
2. 手动触发 WAL 清理(阻塞因素解决后)
-- 1. 切换WAL日志(生成新的WAL段,让旧段进入可回收状态)
SELECT pg_switch_wal();
-- 2. 手动触发检查点(加速WAL回收,检查点会将WAL中的数据刷到数据文件)
CHECKPOINT;
-- 3. 查看可回收的WAL文件(确认清理是否生效)
SELECT pg_walfile_name(pg_current_wal_insert_lsn()) AS current_wal; -- 当前正在使用的WAL
执行后,PostgreSQL 会自动删除 “已同步、已归档、非当前使用” 的 WAL 文件,pg_wal 目录大小会快速下降。
3. 极端情况:磁盘即将满(紧急释放空间)
如果磁盘已不足 5%,可临时移动部分旧 WAL 文件到其他存储(仅当确认这些文件已同步到备库 + 已归档):
# 1. 查看当前正在使用的WAL文件(主库执行SQL)
SELECT pg_walfile_name(pg_current_wal_insert_lsn()) AS current_wal; -- 比如:0000000100000000000000A1
# 2. 进入pg_wal目录(路径:$PGDATA/pg_wal)
cd /var/lib/postgresql/data/pg_wal
# 3. 移动非当前使用的旧WAL文件到临时目录(保留当前WAL和最近1-2个文件,避免误删)
mkdir -p /tmp/pg_wal_backup
mv 000000010000000000000000 0000000100000000000000A0 /tmp/pg_wal_backup/ # 排除当前WAL(A1)
# 4. 触发清理,确认空间释放
psql -U postgres -c "CHECKPOINT;"
df -h # 查看磁盘空间是否恢复
注意一下下:移动的文件需保留 7 天以上,确认数据库正常后再删除,避免后续需要恢复。
第二步:长期控制 WAL 大小(避免复发)
通过配置优化,让 PostgreSQL 自动控制 WAL 大小,无需手动干预:
1. 调整核心配置参数(postgresql.conf)
编辑
postgresql.conf(路径:$PGDATA/postgresql.conf),修改以下参数,重启数据库生效(或ALTER SYSTEM SET动态生效):# 1. 控制主库保留的WAL大小(仅主从架构需要,默认0)
# 建议设为备库同步所需的最大延迟对应的WAL大小(比如备库最多延迟1小时,按写入速度估算)
# 例:若每小时写入1GB WAL,设为2GB(留缓冲),超过后主库会主动删除旧WAL(备库同步延迟会报错,需监控)
wal_keep_size = 2GB
# 2. 控制WAL段的最大总大小(默认16GB,根据磁盘空间调整)
# 超过该大小会触发检查点,将WAL数据刷到数据文件,之后可回收旧WAL
max_wal_size = 8GB # 若磁盘较小,可设为4-8GB(避免单轮WAL占满磁盘)
# 3. 最小WAL保留大小(默认80MB,无需改)
min_wal_size = 80MB
# 4. 归档配置(开启归档时必须确保成功,否则会堆积)
# 示例1:归档到本地目录(需提前创建目录,权限700,属主postgres)
archive_command = 'cp %p /path/to/archive/%f' # %p=当前WAL路径,%f=WAL文件名
# 示例2:归档失败时重试(避免单次失败导致堆积)
archive_command = 'cp %p /path/to/archive/%f || sleep 5; cp %p /path/to/archive/%f'
# 示例3:归档成功后删除原文件(仅非主从架构,主从架构需保留供备库同步)
# archive_command = 'cp %p /path/to/archive/%f && rm %p'
# 5. 检查点间隔(默认5分钟,可缩短,让WAL更频繁回收)
checkpoint_timeout = 5min # 触发检查点的时间间隔
checkpoint_completion_target = 0.9 # 检查点完成时间占间隔的比例(避免IO峰值)
- 动态生效命令(不用重启数据库):
-
ALTER SYSTEM SET wal_keep_size = '2GB'; ALTER SYSTEM SET max_wal_size = '8GB'; SELECT pg_reload_conf(); -- 重载配置
2. 主从架构优化
- 用 “复制槽” 替代
wal_keep_size:复制槽会自动跟踪备库的同步进度,主库只保留备库未同步的 WAL,避免过度保留(推荐 PostgreSQL 10 + 使用): -
-- 主库创建复制槽(备库连接时指定该槽) SELECT pg_create_physical_replication_slot('repl_slot'); -- 备库连接字符串添加复制槽:host=主库IP port=5432 user=repl password=xxx dbname=xxx replication_slot=repl_slot - 监控备库同步延迟:设置告警(比如延迟超过 30 分钟告警),避免备库异常导致 WAL 堆积。
3. 归档备份优化
- 定期清理归档的 WAL 文件:归档的 WAL 文件用于 PITR,建议保留 “基础备份 + 7 天 WAL”(根据业务备份策略调整),超过期限的归档文件可自动删除(比如用脚本定期清理);
- 确保归档存储可用:监控归档目录的磁盘空间,避免存储满导致归档失败。
4. 日常监控
添加以下监控指标,提前发现 WAL 堆积风险:
- pg_wal 目录大小:设置阈值(比如超过 max_wal_size 的 1.5 倍告警);
- 备库同步延迟:
replay_lsn与sent_lsn的差距(超过 1GB 告警); - 归档状态:
pg_stat_archiver的failed_count(大于 0 告警); - 长事务:运行超过 30 分钟的事务(告警并排查)。
四、常见的小误区(避坑哦!)
- 误区 1:直接删除 pg_wal 文件 → 后果:数据库崩溃,无法启动,可能丢数据;
- 误区 2:把
wal_keep_size设为 0 就万事大吉 → 主从架构下,备库短暂延迟可能导致同步失败,需结合复制槽使用; - 误区 3:修改
max_wal_size就能限制堆积 → 若归档 / 同步卡住,max_wal_size无效,核心是确保清理机制正常; - 误区 4:禁用 WAL → 绝对禁止!PostgreSQL 无法禁用 WAL(除非单用户模式维护),禁用后数据无保障。
总结一下下
- pg_wal 文件不能直接删,堆积是 “清理机制阻塞” 导致(主从延迟、归档失败、长事务等);
- 紧急处理:先排查并解决阻塞因素,再执行
pg_switch_wal()+CHECKPOINT触发清理; - 长期控制:通过
wal_keep_size+max_wal_size配置,优化主从 / 归档,监控关键指标; - 核心原则:确保 WAL 的 “同步(主从)” 和 “归档(备份)” 正常,PostgreSQL 会自动清理过期 WAL,无需手动干预。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)