openGauss日志系统详解:从WAL到审计日志的故障排查实战
📝 文章摘要:生产环境出了问题,第一反应就是看日志。但 openGauss 的日志体系比 MySQL 复杂得多 —— 有运行日志、WAL 日志、审计日志、慢查询日志、WDR 报告,五类日志各有不同的用途和排查方法。本文从一次真实的"数据库响应突然变慢"的故障排查出发,演示了如何通过五类日志层层递进定位问题根因。同时也总结了 WAL 损坏修复、审计日志配置、日志轮转等高频运维场景的操作。
⏱ 预计阅读时间:16 分钟(全文约 4,600 字)
🎯 一个真实的故障排查场景
2025 年 4 月的一个周二下午,订单创建接口的 P99 延迟从正常的 45ms 飙升到了 3.2 秒,持续了大约 10 分钟。
没有告警、没有报错、连接数正常、CPU 正常 —— 一切指标看起来都正常,但业务就是慢。
这次排查让我把 openGauss 的五类日志全部翻了一遍。下面沿着排查路径来讲每类日志的用途。
🗺️ openGauss 五类日志总览

日志位置速查
| 日志类型 | 存储位置 | 默认开启 | 保留策略 |
|---|---|---|---|
| 运行日志 | $PGDATA/pg_log/ |
✅ | 按天轮转,保留 30 天 |
| WAL 日志 | $PGDATA/pg_wal/ |
✅ | 自动清理(有复制槽则保留) |
| 审计日志 | $PGDATA/pg_audit/ |
❌ | 需要开启 |
| 慢查询日志 | pg_log 中标记 |
❌ | 需要开启 log_min_duration_statement |
| WDR 快照 | 系统表 dbe_perf |
❌ | 需要安装插件 |
🔍 排查路径:从慢查询到 WAL 的"破案"过程
第一步:运行日志 —— 有没有报错?
# 先看运行日志,有没有 ERROR 或 FATAL
grep -E "ERROR|FATAL|WARNING" $PGDATA/pg_log/postgresql-2025-04-15_*.log | tail -50
运行日志是第一站,因为它记录了所有连接、权限、内存、I/O 相关的错误信息。
当天找到的内容只有一条 WARNING:
2025-04-15 14:23:17 CST WARNING: checkpointer process (PID 28765) wait for WAL flush for 12.345 seconds
WAL flush 等待了 12 秒 —— 这不太正常,但还不是 ERROR,不会触发告警。
第二步:慢查询日志 —— 谁在慢?
# 开启慢查询日志(临时开启,不需要重启)
gs_guc reload -D $PGDATA -c "log_min_duration_statement = 500" # 记录超过 500ms 的 SQL
# 过 5 分钟后查看
cat $PGDATA/pg_log/postgresql-2025-04-15_*.log | grep "duration:" | sort -t: -k2 -rn | head -10
输出:
2025-04-15 14:28:31 CST LOG: duration: 8245.123 ms statement: UPDATE orders SET status='shipped' WHERE id=1234567
2025-04-15 14:28:29 CST LOG: duration: 6123.456 ms statement: UPDATE orders SET status='shipped' WHERE id=1234568
2025-04-15 14:28:27 CST LOG: duration: 5234.789 ms statement: UPDATE orders SET status='shipped' WHERE id=1234569
规律:全部是 UPDATE orders SET status='shipped',逐条执行,每条约 5-8 秒。但 id 上有主键索引,按主键更新怎么会慢?
第三步:看等待事件 —— 卡在哪里了?
-- 查看当前会话的等待事件
SELECT
pid,
wait_event_type,
wait_event,
state,
query
FROM pg_stat_activity
WHERE state = 'active'
AND wait_event IS NOT NULL;
输出:
pid | wait_event_type | wait_event | state | query
--------+-----------------+---------------|--------|-------------------------
28765 | IO | WALWriteLock | active | UPDATE orders SET ...
28766 | IO | WALWriteLock | active | UPDATE orders SET ...
28767 | IO | WALWriteLock | active | UPDATE orders SET ...
所有更新都在等 WALWriteLock。
第四步:WAL 分析 —— 为什么 WAL 写不动了?
# 查看 WAL 目录情况
ls -lh $PGDATA/pg_wal/ | head -20
发现 WAL 目录下有大量未归档的文件,而且最近几个 WAL 文件的大小异常:
-rw------- 1 omm omm 64M Apr 15 14:30 0000000100000000000000AB
-rw------- 1 omm omm 64M Apr 15 14:28 0000000100000000000000AC
-rw------- 1 omm omm 64M Apr 15 14:25 0000000100000000000000AD
每 2 分钟写满一个 64MB 的 WAL 段,正常的订单系统大约是 5-8 分钟写满一个。
-- 查看 WAL 生成速率
SELECT
pg_size_pretty(
pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')::BIGINT
) AS total_wal_written;
重启查询对比,发现 WAL 生成速率是平时的 3 倍。
第五步:根因定位 —— 大量 UPDATE 导致 WAL 暴增
继续深挖,发现这些 UPDATE 来自一个刚上线的批量脚本,它在循环更新订单状态:
// 有问题的代码
for (Order order : pendingOrders) {
// 每次循环都提交一次事务
jdbcTemplate.update("UPDATE orders SET status = ? WHERE id = ?",
"shipped", order.getId());
}
每次循环生成一个独立的事务,每个事务都要写 WAL,10 万笔订单就是 10 万次 WAL 写入。
更有问题的是,status 字段上有一个 B-tree 索引,每次 UPDATE 既要改表数据(写入 WAL),又要改索引(也写入 WAL)。
修复:改为批量提交:
// 修复后:每 1000 条提交一次
int batchSize = 0;
for (Order order : pendingOrders) {
jdbcTemplate.update("UPDATE orders SET status = ? WHERE id = ?",
"shipped", order.getId());
batchSize++;
if (batchSize % 1000 == 0) {
// 手动提交(假设 autoCommit = false)
connection.commit();
}
}
connection.commit(); // 提交剩余的
改了之后,WAL 生成速率从 32MB/min 降到了 8MB/min,WALWriteLock 等待消失了,接口延迟恢复到 45ms。
📝 运行日志配置
# postgresql.conf — 运行日志配置
log_destination = 'csvlog' # 推荐 CSV 格式,方便导入分析
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_file_mode = 0600
log_truncate_on_rotation = off
log_rotation_age = 1d # 每天轮转
log_rotation_size = 100MB # 或 100MB 轮转
log_min_messages = WARNING # 只记录 WARNING 及以上的日志
日志分析技巧
# 1. 统计各类型的日志数量
grep -oP '(?<= )[A-Z]+(?=:)' $PGDATA/pg_log/*.log | sort | uniq -c | sort -rn
# 2. 按小时统计 ERROR 数量
grep "ERROR" $PGDATA/pg_log/*.log | grep -oP '\d{4}-\d{2}-\d{2} \d{2}' | sort | uniq -c
# 3. 找到最耗时的 10 条 SQL
grep "duration:" $PGDATA/pg_log/*.log | \
sed 's/.*duration: //' | sort -t' ' -k1 -rn | head -10
🔒 审计日志配置
信创合规要求数据库必须有审计日志。openGauss 的审计日志比 MySQL 的 audit log 更细粒度。
# postgresql.conf — 审计日志配置
audit_enabled = on # 开启审计
audit_directory = 'pg_audit'
audit_file_mode = 0600
audit_rotation_age = 1d # 每天轮转
audit_rotation_size = 200MB
# 审计事件类型
audit_dml_state = on # INSERT/UPDATE/DELETE
audit_ddl_state = on # CREATE/ALTER/DROP
audit_login_logout = on # 登录/登出
audit_special_function = on # 特殊函数(如 pg_read_file)
audit_system_function = on # 系统函数
audit_user_locked = on # 用户锁定事件
# 排除某些用户或表的审计(减少日志量)
audit_whitelist_users = 'monitor,backup' # 不审计监控用户
审计日志查询
-- 查询审计日志(通过系统函数)
SELECT * FROM pg_query_audit(
'2025-04-15 00:00:00',
'2025-04-15 23:59:59'
) WHERE operation_type = 'INSERT'
ORDER BY entry_id DESC;
审计日志轮转与清理
#!/bin/bash
# /opt/scripts/clean_audit_logs.sh — 保留 90 天
find $PGDATA/pg_audit/ -name "*.csv" -mtime +90 -delete
💥 踩坑:开启审计后性能下降
开启全部审计事件后,TPS 下降了约 15%。原因是每次 DML 操作都要额外写审计日志。
优化方案:
# 只审计关键操作
audit_dml_state = off # 关闭常规 DML 审计
audit_ddl_state = on # 保留 DDL 审计(表结构变更)
audit_login_logout = on # 保留登录审计
audit_special_function = on # 保留敏感操作
-- 创建审计策略(针对敏感表单独审计)
CREATE AUDIT POLICY audit_order_policy
PRIVILEGES ALL
WHEN ON TABLE orders
ADD TO AUDIT LOG;
💥 WAL 损坏修复
这是最让人紧张的场景 —— WAL 文件损坏导致数据库无法启动。
现象
# 启动时遇到这个错误
FATAL: WAL file is corrupted: invalid record length at 0/AB123456
修复方案
# 1. 先备份损坏的 WAL(以防万一)
cp $PGDATA/pg_wal/0000000100000000000000AB /tmp/
# 2. 确认最新的完整检查点
gs_ctl status -D $PGDATA
# 输出会显示最新的检查点 LSN
# 3. 跳过损坏的 WAL,继续恢复
# 修改 postgresql.conf
recovery_target_time = '' # 不指定时间点
ignore_checksum_failure = on # 跳过校验失败
# 4. 如果还是不行,重置 WAL(会丢失数据,谨慎!)
gs_ctl reset_wal -D $PGDATA -l 0000000100000000000000AB
预防 WAL 损坏的最佳实践
# 1. 开启 WAL 校验
wal_checksums = on # 默认就是 on
# 2. 使用可靠的存储(RAID 10 + BBU 缓存)
# 不要用 NFS、不要用 Ceph RBD 做 WAL 存储
# 3. 把 WAL 放到独立的 SSD 上(硬件隔离)
# 配置 postgresql.conf
wal_sync_method = fdatasync # 默认
# 或者用 O_DIRECT
# wal_sync_method = open_sync
🔄 日志轮转与磁盘保护
日志写满磁盘导致数据库不可用,是我见过最冤的事故。
#!/bin/bash
# /opt/scripts/rotate_logs.sh — 每天凌晨执行
PGDATA="/data/opengauss/data"
RETENTION_DAYS=30
# 1. 运行日志
find $PGDATA/pg_log/ -name "*.csv" -mtime +$RETENTION_DAYS -delete
find $PGDATA/pg_log/ -name "*.log" -mtime +$RETENTION_DAYS -delete
# 2. 审计日志
find $PGDATA/pg_audit/ -name "*.csv" -mtime +90 -delete
# 3. 检查磁盘水位
DISK_USAGE=$(df -h $PGDATA | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 80 ]; then
# 触发告警
curl -X POST "http://alert.company.com/api/alert" \
-H "Content-Type: application/json" \
-d "{\"level\":\"warning\",\"message\":\"数据盘使用率 ${DISK_USAGE}%\"}"
fi
# 4. WAL 目录监控
WAL_SIZE_MB=$(du -sm $PGDATA/pg_wal/ | awk '{print $1}')
if [ "$WAL_SIZE_MB" -gt 10240 ]; then # 超过 10GB
# 触发告警(可能复制槽异常)
fi
❓ 常见问题
Q1:运行日志和审计日志的区别是什么?
| 运行日志 | 审计日志 | |
|---|---|---|
| 目的 | 排查错误和异常 | 安全追溯和合规 |
| 记录内容 | 错误、警告、慢查询 | 谁在什么时间做了什么操作 |
| 保留周期 | 30 天 | 90 天(或更长) |
| 谁查 | DBA、开发 | 安全审计员、监管机构 |
Q2:WAL 文件太多怎么办?
-- 检查哪些 WAL 没有被消费
SELECT slot_name, restart_lsn, pg_size_pretty(
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)
) AS unconsumed_wal
FROM pg_replication_slots;
如果某个复制槽的 unconsumed_wal 很大,说明对应的备库掉线了。备库恢复前要评估是否删除该复制槽。
Q3:审计日志会不会影响性能?
会。实测全量审计开启后 TPS 下降约 15%。建议只审计 DDL + 登录 + 敏感操作,不要审计所有 DML。
Q4:慢查询日志找不到怎么办?
确认 log_min_duration_statement 是否设置,并且 logging_collector = on 开启了。慢查询日志跟运行日志混在一起的,用 grep "duration:" 过滤。
🔄 日志产生的完整生命周期
从 SQL 执行到 WAL 清理,一条日志数据在 openGauss 内部经历了以下完整流程:

生命周期关键节点说明
| 阶段 | 进程/组件 | 说明 | 排查要点 |
|---|---|---|---|
| ① SQL 执行 | SQL 解析器 → 执行引擎 | 应用发送 SQL,生成执行计划并修改数据页 | pg_stat_activity 查看当前执行 |
| ② WAL 写入 | WAL 缓冲区 → WAL 文件 | 事务提交时将 WAL 记录从内存刷到磁盘 | WALWriteLock 等待、pg_wal/ 目录大小 |
| ③ 检查点 | 检查点进程 | 将共享缓冲区脏页刷盘,记录 REDO 起点 | 检查点频率过高会触发大量 I/O |
| ④ 归档 | 归档进程 | 将写满的 WAL 段拷贝到远程存储 | 归档失败会导致 WAL 积压 |
| ⑤ 清理 | 清理进程 | 删除已归档且检查点已过的 WAL 文件 | 复制槽未消费或归档失败会阻止清理 |
📝 总结
那次 WALWriteLock 的故障排查让我深刻理解了 openGauss 的日志体系:
- 运行日志 — 快速定位 ERROR 和 FATAL
- 慢查询日志 — 找到那些拖垮性能的"坏 SQL"
- 等待事件 — 分析数据库到底"卡"在哪个环节
- WAL 分析 — 定位写入瓶颈和数据量异常的根因
- 审计日志 — 安全合规必备
日志不是越多越好,而是关键时候找得到有用的。
配置日志时有三个核心原则:
- 运行日志:记录所有 WARNING 以上,保留 30 天
- 慢查询日志:记录超过 500ms 的 SQL,用于日常优化
- 审计日志:只审计 DDL + 登录 + 敏感操作,减少性能损耗
下一篇文章讲 openGauss 集群的日常运维和故障快速恢复,包括备份恢复、巡检脚本、健康检查。
💬 互动:你在数据库故障排查中,最常用到哪类日志?有没有过"日志什么都查不出来,最后发现是 XX 问题"的经历?
- 点赞
- 收藏
- 关注作者
评论(0)