PostgreSQL WAL 日志归档 + 压缩

一、核心前提:为什么一定要给 WAL 归档做压缩?

 

✅ 压缩的核心价值(生产环境必做,无任何副作用)

 
PostgreSQL 的 WAL(Write-Ahead Log)预写日志默认是 16MB / 个的纯二进制文件,未压缩的 WAL 文件冗余度极高
 
  1. 压缩率惊人:常规业务场景压缩率 90%+,16MB 的 WAL 文件压缩后仅 1~2MB;高写入场景压缩率也能达到 70%+,极大节省磁盘空间;
  2. 节省带宽:如果是远程归档(归档到另一台服务器 / NAS / 对象存储),压缩后传输的数据量大幅减少,避免带宽占满;
  3. 无性能损耗:压缩是异步归档阶段执行,不会影响数据库的主业务读写性能(WAL 写入是数据库核心流程,压缩在 WAL 文件关闭后执行);
  4. PG 原生完美支持:压缩后的 WAL 归档文件,在数据库恢复 / 主备同步时,会自动识别并解压,无需手动处理,对恢复逻辑完全透明。
 

✅ 关键认知

 
  1. WAL 日志的归档,核心配置是 archive_command 这个参数,WAL 的压缩逻辑,就是直接嵌入在这个归档命令中,无需安装任何插件、无需修改 PG 源码,纯原生实现;
  2. 压缩后的 WAL 文件命名建议保留原文件名 + 压缩后缀(如.gz/.xz),PG 的恢复机制能自动识别;
  3. 主流压缩方式:gzip(✅ 99% 场景首选) > xz(极致压缩) > lz4(极速压缩),三者都适配 PG 归档,下面会逐一讲解。
 

 

二、前置基础:先开启 PG 的 WAL 归档(必配,压缩的前提)

 
压缩是基于「归档」的,必须先开启 WAL 归档功能,再在归档命令中加入压缩逻辑,所有配置均在 PG 的数据目录 $PGDATA 下的 postgresql.conf 文件中修改,修改后需要重启 PostgreSQL 服务生效
 

✅ 1. 核心归档配置(postgresql.conf 必改项)

 
# ========== 归档核心开关 ==========
# 归档模式开启(必须为on,归档的总开关)
archive_mode = on
# WAL日志级别,归档的最低要求,必须设置为 replica 或 higher (9.6+版本)
wal_level = replica

# ========== 归档命令占位 ==========
# 这里先留空,后面配置「归档+压缩」的完整命令,这是核心配置项
archive_command = ''

# ========== 可选优化配置 ==========
# WAL文件默认大小,默认16MB,无需修改,修改会影响性能
wal_segment_size = 16MB
# 可选:如果长时间无WAL切换(比如只读库),强制多少秒归档一次,避免归档延迟,单位秒
archive_timeout = 60
# WAL写入缓冲区,默认即可,不用改
wal_buffers = 16MB
 
 

✅ 2. 重启 PG 服务生效

 
# CentOS/RHEL/Rocky Linux
systemctl restart postgresql-14  # 替换为你的PG版本
# Debian/Ubuntu
systemctl restart postgresql
# 通用方式
pg_ctl restart -D $PGDATA -m fast
 
 

 

三、方案一:gzip 压缩归档(✅ 首选!生产环境 99% 用这个)

 

✅ 为什么首选 gzip?

 
  • 压缩 / 解压速度快,CPU 消耗极低,几乎不影响服务器性能;
  • 压缩率优秀,16MB 的 WAL 文件压缩后≈1.5MB,压缩率 90%+;
  • 系统原生自带,所有 Linux 发行版(CentOS/Ubuntu/Debian)都预装了 gzip,无需额外安装;
  • 解压效率远高于 xz,在数据库恢复时,解压速度快能减少恢复耗时,这是生产环境的核心考量。
 

✅ 配置方式(核心!2 种常用写法,按需选择)

 
修改 postgresql.conf 中的 archive_command 参数即可,无需重启 PG,执行 pg_ctl reload -D $PGDATA 重载配置生效!
 

✔ 写法 1:【本地归档 + 压缩】最常用,归档到本地目录(推荐)

 
将 WAL 日志压缩后,归档到本地的指定目录(比如 /pg_archive),必须给 postgres 用户授权该目录的读写权限
# 第一步:创建归档目录,postgres用户必须有读写权限(提前执行)
# mkdir -p /pg_archive && chown -R postgres:postgres /pg_archive && chmod 700 /pg_archive

# 核心配置:archive_command 配置gzip压缩+归档
# %p = WAL日志的源文件绝对路径(如 $PGDATA/pg_wal/000000010000000000000001)
# %f = WAL日志的纯文件名(如 000000010000000000000001)
archive_command = 'gzip -6 %p -c > /pg_archive/%f.gz'
 
 
命令参数说明
 
  • gzip -6:压缩级别,取值 1~9,6 是默认值;1 = 最快压缩(压缩率稍低),9 = 最高压缩率(稍耗 CPU),生产建议用 6 即可;
  • -c:关键参数,stdout 标准输出,将压缩后的内容输出到指定文件,而不是替换原文件;
  • > /pg_archive/%f.gz:将压缩后的内容写入归档目录,文件名保留原 WAL 名 +.gz后缀,便于识别。
 

✔ 写法 2:【归档 + 压缩 + 容错 + 日志】生产最优完整版(强烈推荐!)

 
上面的基础写法如果归档失败,不会有任何日志,生产环境建议加容错判断 + 日志记录,避免 WAL 归档失败堆积导致磁盘满,同时能排查归档失败原因: 
# 归档成功 → 静默;归档失败 → 将错误信息写入日志文件 /pg_archive/archive_fail.log
archive_command = 'gzip -6 %p -c > /pg_archive/%f.gz 2>> /pg_archive/archive_fail.log || echo "归档失败: %f 时间: $(date)" >> /pg_archive/archive_fail.log'
 
 

✔ 写法 3:【远程归档 + 压缩】归档到远程服务器(scp 方式,跨机备份)

 
如果需要将压缩后的 WAL 归档到另一台远程服务器(灾备必备),结合scp命令即可,前提是配置 postgres 用户的免密 ssh 登录(避免归档时输密码):
 
# 本地压缩后,通过scp上传到远程服务器的归档目录
archive_command = 'gzip -6 %p -c | scp -q -C /dev/stdin postgres@192.168.1.200:/pg_archive/%f.gz'
 
 
免密 ssh 配置:在 PG 服务器执行 su - postgresssh-keygen -t rsassh-copy-id postgres@192.168.1.200
 
 

 

四、方案二:xz 压缩归档(极致压缩,磁盘极度紧张场景)

 

✅ xz 的特点

 
  • 压缩率比 gzip 更高:16MB 的 WAL 文件压缩后≈800KB,压缩率 95%+,极致节省磁盘空间;
  • 缺点:压缩速度比 gzip 慢,CPU 消耗稍高,解压速度也比 gzip 慢;
  • 适用场景:磁盘空间非常紧张、WAL 归档量极大、对 CPU 消耗不敏感、对恢复速度要求不高的场景(比如离线备份归档);
  • 系统预装:大部分 Linux 发行版自带 xz,若没有则执行 yum install xz -y / apt install xz-utils -y 安装。
 

✅ 配置方式(替换 archive_command 即可)

# 本地xz压缩归档,-6为默认压缩级别,-9为最高压缩率
archive_command = 'xz -6 %p -c > /pg_archive/%f.xz'

# 生产完整版(容错+日志)
archive_command = 'xz -6 %p -c > /pg_archive/%f.xz 2>> /pg_archive/archive_fail.log || echo "归档失败: %f 时间: $(date)" >> /pg_archive/archive_fail.log'
 
 

 

五、方案三:lz4 压缩归档(极速压缩,超高写入性能场景)

 

✅ lz4 的特点

 
  • 压缩 / 解压速度极快,是三者中速度最快的,CPU 消耗极低,几乎可以忽略不计;
  • 压缩率略低于 gzip:16MB 的 WAL 文件压缩后≈3MB,压缩率 80%+;
  • 适用场景:高写入性能的核心业务库、对 CPU 和 IO 要求极高、WAL 生成速度极快的场景(比如每秒生成多个 WAL 文件);
  • 注意:部分系统默认没有 lz4,需要手动安装:yum install lz4 -y / apt install lz4 -y
 

✅ 配置方式(替换 archive_command 即可)

# 本地lz4压缩归档,-1为默认压缩级别,lz4的压缩级别对速度影响很小
archive_command = 'lz4 -1 %p -c > /pg_archive/%f.lz4'

# 生产完整版(容错+日志)
archive_command = 'lz4 -1 %p -c > /pg_archive/%f.lz4 2>> /pg_archive/archive_fail.log || echo "归档失败: %f 时间: $(date)" >> /pg_archive/archive_fail.log'
 
 

 

六、核心关键:压缩后的 WAL 日志,恢复时是否需要手动解压?

 

✅ ✔ 答案:完全不需要!PG 原生自动识别 + 自动解压

 
这是最核心的知识点,也是大家最关心的问题,PostgreSQL 在执行恢复(recovery)时,会自动识别归档的 WAL 文件后缀
 
  1. 如果是 .gz 后缀 → 自动调用 gunzip 解压;
  2. 如果是 .xz 后缀 → 自动调用 unxz 解压;
  3. 如果是 .lz4 后缀 → 自动调用 lz4 -d 解压;
  4. 解压是透明执行的,恢复逻辑和未压缩的 WAL 文件完全一致,无需修改 recovery.conf/recovery.signal 配置,无需任何额外操作。
 

✅ 恢复的核心原理

 
PG 的恢复过程中,会读取归档目录中的 WAL 文件,根据文件后缀判断是否为压缩文件,然后调用对应的解压命令将内容读取到内存,再应用 WAL 日志,整个过程对用户无感知。
 

 

七、WAL 归档压缩的 生产级优化配置 & 最佳实践(必看,避坑 + 提效)

 

✅ ✔ 最佳实践 1:归档目录的权限与目录规划

 
  1. 归档目录必须给 postgres:postgres 属主,权限设置为 700,禁止其他用户访问,避免 WAL 文件被篡改:
     
    chown -R postgres:postgres /pg_archive
    chmod 700 /pg_archive
    
     
     
  2. 建议归档目录和 PG 数据目录不在同一个磁盘,避免归档文件占满数据盘导致数据库崩溃;
  3. 可以按日期分目录归档,便于管理和清理(比如 /pg_archive/202601),适合归档量极大的场景:
    archive_command = 'mkdir -p /pg_archive/$(date +\%Y\%m) && gzip -6 %p -c > /pg_archive/$(date +\%Y\%m)/%f.gz'
    
     
     
    注意:date +%Y%m 中的%需要转义为\%,因为 PG 的配置文件中%是特殊字符。
     
     
 

✅ ✔ 最佳实践 2:压缩级别的选择(核心调优)

 
  • gzip:生产用 -6(默认)即可,平衡压缩率和速度;如果 CPU 充足,可改为 -9(最高压缩);如果 CPU 紧张,改为 -1(最快压缩);
  • xz:建议用 -6,-9 的压缩率提升有限,但 CPU 消耗翻倍;
  • lz4:固定用 -1,压缩级别对压缩率影响极小,速度优先。
 

✅ ✔ 最佳实践 3:归档失败的监控与容错(生产必备)

 
  1. 上面的归档命令中已经加入了日志记录,建议定期查看 /pg_archive/archive_fail.log 日志,排查归档失败原因;
  2. 归档失败的核心后果:WAL 文件会堆积在 $PGDATA/pg_wal 目录,如果长期失败会导致磁盘满,数据库崩溃;
  3. 监控指标:检查 pg_wal 目录的文件数量,如果持续增加,说明归档失败,立即排查。
 

✅ ✔ 最佳实践 4:归档文件的定期清理(重中之重!)

 
压缩后的 WAL 文件依然会持续堆积,必须定期清理归档目录的旧 WAL 文件,否则磁盘迟早会满!
 
  1. 清理原则:只清理比当前数据库最新 LSN 更早的 WAL 文件,或者根据业务需求保留指定天数(比如保留 7 天归档,足够恢复即可);
  2. 推荐用 PG 自带的 pg_archivecleanup 工具清理,安全无风险,不会误删正在使用的 WAL 文件:
     
    # 清理 /pg_archive 目录中,比 000000010000000000000020 更早的WAL文件
    pg_archivecleanup /pg_archive 000000010000000000000020
    
     
     
  3. 生产建议:写一个定时任务(crontab),每天凌晨清理 7 天前的归档文件:
     
    # 编辑postgres用户的定时任务
    su - postgres
    crontab -e
    # 添加一行:每天凌晨2点清理归档目录中7天前的WAL.gz文件
    0 2 * * * find /pg_archive -name "*.gz" -mtime +7 -delete
    
     
     
 

✅ ✔ 最佳实践 5:避免归档命令阻塞数据库

 
归档命令是同步执行的,如果归档命令执行缓慢(比如远程 scp 网络卡顿),会阻塞后续的 WAL 归档,导致 WAL 堆积。
 
解决方案:使用异步归档工具(如 pg_archivewalwal-g),生产环境高可用建议使用,能极大提升归档的稳定性。
 

 

八、如何验证 WAL 归档 + 压缩 是否生效?(3 步验证,必做)

 
配置完成后,必须验证是否生效,避免配置错误导致归档失败,3 个步骤,简单高效
 

✔ 步骤 1:重载配置(修改 archive_command 后必做)

  
pg_ctl reload -D $PGDATA
# 或
systemctl reload postgresql-14
 
 

✔ 步骤 2:手动触发 WAL 切换,生成新的 WAL 文件 

su - postgres
# 执行切换命令,强制生成新的WAL文件,触发归档
psql -c "SELECT pg_switch_wal();"
 
 

✔ 步骤 3:查看归档目录是否有压缩后的 WAL 文件 

ls -lh /pg_archive/
 
 
✅ 生效的表现:归档目录中出现类似 000000010000000000000001.gz 的文件,文件大小≈1~2MB,说明归档 + 压缩成功!
 

 

九、常见错误排查(归档压缩失败的高频原因,99% 的坑都在这)

 

❌ 错误 1:归档目录权限不足 → 最常见!

 
日志报错:permission denied,归档目录的属主不是 postgres,导致无法写入文件。
 
✅ 解决方案: 
chown -R postgres:postgres /pg_archive
chmod 700 /pg_archive
 
 

❌ 错误 2:压缩命令不存在(比如 lz4 未安装)

 
日志报错:command not found,系统中没有对应的压缩命令。
 
✅ 解决方案: 
# gzip
yum install gzip -y
# xz
yum install xz -y
# lz4
yum install lz4 -y
 
 

❌ 错误 3:archive_command 配置错误,% p/% f 写反

 
比如写成 gzip %f -c > /pg_archive/%p.gz,% f 是纯文件名,没有路径,会导致找不到文件。
 
✅ 解决方案:记住固定规则:%p = 源文件完整路径%f = 源文件纯名称永远是 gzip % p -c > 归档目录 /% f.gz
 

❌ 错误 4:WAL 文件堆积在 pg_wal 目录,归档目录无文件

 
说明归档命令执行失败,查看归档失败日志 /pg_archive/archive_fail.log,根据日志信息排查即可。
 

❌ 错误 5:压缩级别写错(比如 gzip -10)

 
gzip 的压缩级别是 1~9,写 10 会报错,改为 1~9 即可。
 

 

十、三种压缩方案的对比 & 生产选型建议(一目了然)

 
压缩方式压缩率压缩速度解压速度CPU 消耗适用场景推荐度
gzip 90%+ 极快 极低 99% 生产环境、通用场景、主备同步 ✅✅✅ 强烈推荐
xz 95%+ 较慢 较慢 中等 磁盘极度紧张、离线备份、归档量极大 ✅✅ 可选
lz4 80%+ 极快 极快 极低 高写入性能库、CPU 紧张、极速归档 ✅✅ 高性能场景首选
 

✅ 最终选型结论

 
无脑选 gzip 即可,满足 99% 的生产场景需求,平衡压缩率、速度、CPU 消耗,兼容性最好,无任何坑!
 
 
 

posted on 2026-01-15 15:33  数据库那些事儿  阅读(0)  评论(0)    收藏  举报