前言

在人大金仓 V9 的生产运维中,内存异常上涨、CPU 突发飙升、磁盘突然爆满是最高发的三类致命故障,轻则导致业务响应变慢,重则直接引发数据库 OOM 宕机、业务写入中断。很多运维人员遇到这类问题时,往往只会盲目重启、扩容硬件,找不到根本原因,导致故障反复出现。

本文基于官方运维手册 + 12+ 政企生产项目排障经验,从底层原理到排查路径,再到根治方案,系统性拆解三大顽疾。所有命令、参数、优化方案均经过生产环境实测验证,可直接复制落地,帮助你快速定位、彻底解决问题。


一、通用排查方法论:自底向上四步定位法

所有资源类故障都遵循「先系统、后数据库,先宏观、后微观」的排查逻辑,避免一上来就扎进 SQL 细节,浪费排障时间:

  1. 系统层排查:先用 topiostatdf -h 确认是哪个资源打满,以及是否是数据库进程导致的
  2. 进程层排查:定位是数据库主进程占用,还是单个后端进程、外部进程占用
  3. 实例层排查:进入数据库,查看连接数、锁等待、配置参数、后台任务状态
  4. SQL 层排查:定位具体的慢 SQL、大事务、异常会话,针对性优化

💡 排障核心原则:先恢复业务,再排查根因;保留现场信息,禁止盲目重启。


二、内存异常上涨 / 内存泄漏深度根治

很多人遇到内存持续上涨就说是「内存泄漏」,实际上 90% 的场景都是参数配置不合理、会话资源未释放导致的,真正的内核级内存泄漏极少。

🧠 2.1 典型故障现象

  • 数据库进程内存占用持续增长,不会自动回落
  • 系统 Swap 使用率持续升高,业务响应变慢
  • 系统日志出现 OOM Killer 杀掉数据库进程的记录
  • 数据库日志报错 out of memory内存分配失败

2.2 人大金仓内存架构原理

金仓内存分为两大块,排查时必须分开分析:

  1. 共享内存区:所有进程共享,包括 shared_buffers(数据缓存)、wal_buffers(日志缓存)等,启动时一次性分配,大小固定
  2. 本地进程内存:每个连接会话单独占用,包括 work_mem(排序 / 哈希内存)、maintenance_work_mem(维护操作内存)、游标缓存、临时表缓存等,连接数越多、操作越复杂,占用越高

⚠️ 最常见的误区:只看 shared_buffers,忽略了「连接数 × work_mem」的累加值,这往往是内存暴涨的元凶。

2.3 根因逐层排查

第一步:系统层确认
# 1. 查看系统整体内存使用,确认是 kingbase 进程占用
top -b -n 1 | head -20
# 按内存排序,查看 top 进程
top -o %MEM

# 2. 查看数据库进程的详细内存占用
ps -eo pid,rss,vsz,cmd | grep kingbase | grep -v grep

# 3. 确认 Swap 使用情况
free -m
第二步:参数合理性校验

计算理论最大内存占用,判断是否参数配置超标:

理论最大内存 ≈ shared_buffers + max_connections × work_mem + maintenance_work_mem × 并发维护数 + 系统预留

超标典型场景:32G 内存,shared_buffers 设了 16G,work_mem 设了 64M,最大连接数 500,仅 work_mem 理论上限就达 32G,远超物理内存。

第三步:会话级内存占用排查
-- 查看长会话、长事务,确认是否有资源未释放
SELECT 
  pid, usename, state,
  now() - backend_start AS 会话时长,
  now() - xact_start AS 事务时长,
  substr(query, 1, 80) AS 当前SQL
FROM sys_stat_activity
ORDER BY backend_start DESC;

-- 查看打开的游标数,游标不释放会持续占用内存
SELECT pid, usename, cursor_name, state 
FROM sys_cursors;
第四步:临时文件与内存上下文排查
-- 查看临时文件占用,work_mem 不足时会写磁盘,也会占用内存映射
SELECT datname, temp_files, temp_bytes 
FROM sys_stat_database;

2.4 四大高频场景解决方案

场景 1:参数配置不合理导致内存溢出

解决方案:按官方规范调整核心内存参数

# shared_buffers:物理内存的 25%~40%,32G 内存建议 8G~12G
shared_buffers = 8GB

# work_mem:单会话排序/哈希内存,建议 4MB~16MB,高并发场景宁小勿大
work_mem = 8MB

# maintenance_work_mem:维护操作内存,建索引、vacuum 用,设 512MB~2GB
maintenance_work_mem = 1GB

# wal_buffers:日志缓存,通常 16MB~64MB
wal_buffers = 32MB

# 有效缓存大小:优化器估算用,设为物理内存的 50%~70%
effective_cache_size = 20GB

✅ 官方规范:shared_buffers 禁止超过物理内存的 40%,剩余内存留给操作系统缓存和连接进程,否则极易触发系统 OOM。

场景 2:长会话 / 游标不释放,内存持续堆积

解决方案

  1. 批量清理空闲超过 1 小时的异常会话:
SELECT pg_terminate_backend(pid)
FROM sys_stat_activity
WHERE state = 'idle' 
  AND state_change < now() - interval '1 hour'
  AND usename <> 'replication';
  1. 配置空闲事务超时,自动释放资源:
# 空闲事务超时,单位毫秒,30分钟自动关闭
idle_in_transaction_session_timeout = 1800000
  1. 业务侧优化:用完游标及时关闭,连接池设置合理的空闲连接回收时间。
场景 3:大排序 / 大哈希操作导致内存暴涨

现象:复杂报表查询、大表关联时,内存瞬间飙升 解决方案

  1. 适当调大 work_mem,避免频繁写磁盘,但不可过大
  2. 优化 SQL,减少大结果集排序,增加索引避免排序
  3. 大报表业务放到备库执行,避免影响主库业务
场景 4:插件 / 扩展导致的真实内存泄漏

现象:业务平稳但内存持续缓慢上涨,重启后恢复,运行一段时间又涨 解决方案

  1. 排查近期新增的插件、自定义函数
  2. 关闭非必要插件,使用官方认证的扩展
  3. 升级到最新小版本,修复已知的内核内存问题

2.5 内存问题长效治理

  1. 配置内存使用率告警,达到 80% 提前预警
  2. 定期巡检长会话、长事务,及时清理异常连接
  3. 上线 SQL 审核,禁止大结果集全表排序、无关联条件的多表关联
  4. 数据库服务器关闭 Swap,避免内存泄漏后系统拖死

三、CPU 突发飙升深度根治

CPU 飙升是最常见的性能故障,90% 以上都和 SQL 执行效率有关,少数为锁等待、后台任务、硬件问题导致。

⚡ 3.1 典型故障现象

  • 数据库服务器 CPU 使用率持续 90%+,业务响应极慢
  • 活跃会话数暴涨,大量 SQL 处于执行状态
  • 接口超时、事务堆积,业务侧大面积报错

3.2 分层排查路径

第一步:系统层确认 CPU 类型
# 查看 CPU 整体使用,确认是 user 态高还是 sys 态高、iowait 高
top -b -n 1 | head -5

# 按 CPU 排序查看进程
top -o %CPU

# 查看每个 CPU 核的负载
mpstat -P ALL 1 3
  • user 态高:大概率是 SQL 执行效率问题,比如全表扫描、执行计划跑偏
  • sys 态高:大概率是系统调用过多、上下文切换频繁,比如连接数暴增
  • iowait 高:本质是 IO 瓶颈,不是 CPU 问题,参考磁盘部分优化
第二步:数据库层定位高消耗 SQL
-- 查看当前正在执行的活跃会话,按执行时长排序
SELECT 
  pid, usename, client_addr,
  now() - query_start AS 执行时长,
  wait_event_type, wait_event,
  substr(query, 1, 150) AS SQL文本
FROM sys_stat_activity
WHERE state = 'active'
ORDER BY 执行时长 DESC
LIMIT 20;
第三步:历史慢 SQL 复盘

需提前开启 sys_stat_statements 扩展:

-- 查看总耗时 TOP20 的 SQL
SELECT 
  calls AS 执行次数,
  round(total_exec_time::numeric, 2) AS 总耗时_ms,
  round(mean_exec_time::numeric, 2) AS 平均耗时_ms,
  shared_blks_hit AS 缓存命中块,
  shared_blks_read AS 磁盘读取块,
  substr(query, 1, 150) AS SQL片段
FROM sys_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

3.3 六大高频场景解决方案

场景 1:统计信息过期,执行计划跑偏(最高发)

现象:原本很快的 SQL 突然变慢,执行计划走全表扫描 根因:表数据量变化大,autovacuum 不及时,统计信息过时,优化器选错执行计划。 解决方案

-- 紧急:对问题表全量收集统计信息
ANALYZE VERBOSE 表名;

-- 全库收集统计信息(业务低峰执行)
ANALYZE;

长效治理:调优 autovacuum 参数,加快统计信息更新频率;大表定期手动收集。

场景 2:慢 SQL 雪崩,大量并发全表扫描

现象:某条慢 SQL 被高频调用,瞬间打满 CPU 解决方案

  1. 紧急止损:先 kill 掉耗时最长的一批慢查询
SELECT pg_terminate_backend(pid)
FROM sys_stat_activity
WHERE state = 'active' 
  AND now() - query_start > interval '5 minute';
  1. 快速优化:给对应 SQL 增加合适的联合索引,避免全表扫描
  2. 热点场景:对高频查询增加 Redis 缓存,降低数据库压力
场景 3:锁等待导致的 CPU 虚高

现象:CPU 高但没看到慢 SQL,大量会话处于等待状态 根因:长事务持有锁,其他会话自旋等待,消耗 CPU 解决方案

-- 查看锁等待
SELECT pid, usename, wait_event_type, wait_event, query
FROM sys_stat_activity
WHERE wait_event_type = 'Lock';

-- 找到阻塞源头,kill 对应长事务

长效治理:缩小事务范围,禁止事务中嵌套外部调用;统一更新顺序,避免死锁。

场景 4:autovacuum 频繁执行占用 CPU

现象:业务高峰期 CPU 莫名升高,后台有大量 autovacuum 进程 根因:频繁更新删除的表产生大量死元组,触发自动清理 解决方案

  1. 调优 autovacuum 参数,错开业务高峰
  2. 业务低峰期手动执行 VACUUM ANALYZE,减轻高峰期压力
  3. 大表分区,降低单表清理成本
场景 5:连接数暴增,上下文切换频繁

现象:CPU sys 态高,业务没明显增长但连接数翻倍 根因:应用连接池配置不合理,突发创建大量连接,或连接风暴 解决方案

  1. 优化应用连接池,设置合理的最小 / 最大连接数,开启连接复用
  2. 数据库端限制最大连接数,配置连接超时
  3. 接入数据库代理,统一管理连接,减少连接风暴影响
场景 6:排序、聚合、去重操作过多

现象:报表、统计类 SQL 占用大量 CPU 解决方案

  1. 建立合适的联合索引,利用索引有序性避免排序
  2. 大报表异步执行,放到备库跑
  3. 预计算汇总结果,用空间换时间

3.4 CPU 长效优化体系

  1. 上线 SQL 审核:所有新增 SQL 必须审核,避免全表扫描、无索引关联
  2. 慢 SQL 闭环治理:每日复盘慢查询,持续优化 TOP 耗时 SQL
  3. SQL 限流熔断:配置 SQL 执行超时,避免单条慢 SQL 拖垮整个库
  4. 定期统计信息收集:每周业务低峰全库收集一次,保障执行计划准确

四、磁盘突然爆满深度根治

磁盘爆满是最容易引发严重故障的场景,尤其是 sys_wal 目录暴涨,往往几十分钟就能占满整块盘,导致数据库挂起、写入中断。

💾 4.1 典型故障现象

  • 监控告警磁盘使用率 100%
  • 数据库写入报错,事务无法提交
  • 数据库进入只读或挂起状态,业务完全中断

4.2 快速定位根因

第一步先定位哪个目录占满了,90% 的场景都集中在 4 个目录:

# 1. 进入数据目录
cd /data/kingbase/data

# 2. 查看各目录大小排序,快速定位大目录
du -sh * | sort -hr | head -10

正常基线base 目录(业务数据)最大,其次是 sys_wal(预写日志),其他目录占比很小。 异常特征

  • sys_wal 目录几十上百 GB → WAL 日志无法回收
  • base/syssql_tmp 目录暴涨 → 临时文件堆积
  • log / sys_aud 目录很大 → 运行日志、审计日志未轮转清理
  • base 里单表异常大 → 表 / 索引膨胀

4.3 四大核心场景解决方案

场景 1:sys_wal 日志暴涨(最高发,占磁盘爆满故障 70%+)

三大根因与对应解法

根因 1:复制槽失效,WAL 无法回收

主备架构中,备库断开或下线后,复制槽未删除,主库会一直保留 WAL 等待备库接收,最终占满磁盘北京人大金仓信息技术股份有限公司。

-- 查看复制槽状态,active=f 就是失效的
SELECT slot_name, slot_type, active, restart_lsn
FROM sys_replication_slots;

-- 删除失效的复制槽,立即释放空间
SELECT pg_drop_replication_slot('失效的槽名');

🚨 生产红线:备库下线后必须同步删除复制槽,否则必然导致磁盘爆满。

根因 2:归档失败,WAL 无法标记为可回收

归档命令执行失败,WAL 日志一直处于待归档状态,无法被循环覆盖。

-- 查看归档状态,failed_count > 0 说明归档失败
SELECT * FROM sys_stat_archiver;

解决方案

  1. 检查归档目录是否存在、权限是否正确、磁盘是否满了
  2. 测试归档命令是否能手动执行成功
  3. 修复归档后,WAL 会自动逐步回收
根因 3:长事务持有老快照,WAL 无法清理

一个运行几小时的长事务,会导致期间所有 WAL 都不能回收。

-- 查找运行超过 1 小时的长事务
SELECT pid, now() - xact_start AS 事务时长, query
FROM sys_stat_activity
WHERE state <> 'idle'
ORDER BY 事务时长 DESC
LIMIT 10;

解决方案:kill 掉异常长事务,WAL 会自动回收;业务侧避免大事务、长事务。

场景 2:表 / 索引膨胀,占用空间远超实际数据

现象:业务数据没涨多少,但表文件越来越大,查询越来越慢 根因:频繁更新删除产生死元组,autovacuum 清理不及时,导致表膨胀北京人大金仓信息技术股份有限公司。

-- 查看死元组占比 TOP20 的表
SELECT 
  schemaname, relname,
  n_live_tup AS 活元组,
  n_dead_tup AS 死元组,
  round(n_dead_tup * 100.0 / (n_live_tup + n_dead_tup + 1), 2) AS 死元组占比
FROM sys_stat_user_tables
ORDER BY 死元组 DESC
LIMIT 20;

解决方案

  1. 紧急处理:业务低峰执行 VACUUM VERBOSE ANALYZE 表名; 回收空间
  2. 膨胀严重:执行 VACUUM FULL 或重建表、重建索引(会锁表,低峰执行)
  3. 长效治理:调优 autovacuum 参数,加快清理频率;大表按时间分区
场景 3:临时文件 syssql_tmp 暴涨

现象base/syssql_tmp 目录瞬间产生几十 GB 临时文件 根因work_mem 设置过小,大表排序、哈希关联时内存不够,被迫写磁盘临时文件。 解决方案

  1. 紧急:kill 掉对应的大查询,临时文件会自动释放
  2. 优化:给对应 SQL 加索引,避免大结果集排序
  3. 参数:适当调大 work_mem,减少磁盘临时文件生成
  4. 长效:大报表迁移到备库执行,不占用主库资源
场景 4:运行日志、审计日志堆积

现象log 目录或 sys_aud 目录持续增长 根因:未配置日志轮转,或日志级别过高,打印大量信息 解决方案

  1. 配置日志自动轮转:
# 单个日志文件 100MB
log_rotation_size = 100MB
# 每天轮转一次
log_rotation_age = 1d
# 轮转时截断旧文件
log_truncate_on_rotation = on
  1. 配置定时脚本,定期删除 30 天前的日志和审计文件
  2. 降低不必要的日志级别,关闭连接、断开日志

4.4 磁盘容量长效治理

  1. 监控告警:磁盘使用率 80% 预警,85% 严重告警,90% 紧急处置
  2. 目录分离:sys_wal、归档、备份、业务数据分盘存放,避免互相影响
  3. 定期清理:制定日志、归档、备份的保留策略,自动清理过期文件
  4. 容量规划:每月评估数据增长趋势,提前规划扩容,避免被动救火

五、生产级一键排查脚本

将以下脚本保存为 kes_resource_check.sh,故障时一键执行,快速输出三大资源的核心信息。

#!/bin/bash
# 人大金仓V9 资源异常一键排查脚本
# 执行用户:kingbase

export KINGBASE_HOME=/opt/Kingbase/ES/V9
export PATH=$KINGBASE_HOME/bin:$PATH
export DATA_DIR=/data/kingbase/data
export DB_USER=system
export PGPASSWORD='your_password'
export DB_NAME=testdb
export DB_PORT=54321

echo "====================================="
echo " 人大金仓 V9 资源异常排查报告"
echo " 生成时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo "====================================="

echo -e "\n=== 1. 系统资源概览 ==="
echo "CPU 负载:$(uptime | awk -F 'load average: ' '{print $2}')"
echo "内存使用:$(free -m | awk 'NR==2{printf "已用%dMB / 总共%dMB,使用率%.2f%%", $3, $2, $3*100/$2}')"
echo "Swap 使用:$(free -m | awk 'NR==4{printf "已用%dMB / 总共%dMB", $3, $2}')"
echo -e "\n磁盘使用率 TOP5:"
df -h | sort -k5 -r | head -5

echo -e "\n=== 2. 数据目录大小分布 ==="
echo "数据目录:$DATA_DIR"
du -sh $DATA_DIR/* 2>/dev/null | sort -hr | head -10

echo -e "\n=== 3. 数据库核心状态 ==="
echo "实例状态:$(sys_ctl status -D $DATA_DIR | grep -E 'running|stopped')"

echo -e "\n连接数情况:"
ksql -U $DB_USER -d $DB_NAME -p $DB_PORT -t -c "
SELECT '总连接数: ' || count(*) || ' / 最大: ' || current_setting('max_connections')::int
FROM sys_stat_activity;
"

echo -e "\n活跃会话 TOP10(执行时长排序):"
ksql -U $DB_USER -d $DB_NAME -p $DB_PORT -c "
SELECT pid, usename, state,
       now() - query_start AS 执行时长,
       substr(query, 1, 80) AS SQL
FROM sys_stat_activity
WHERE state = 'active'
ORDER BY 执行时长 DESC
LIMIT 10;
"

echo -e "\n=== 4. WAL 与归档状态 ==="
ksql -U $DB_USER -d $DB_NAME -p $DB_PORT -c "
SELECT archived_count AS 已归档数, failed_count AS 失败数,
       last_archived_wal AS 最后归档文件
FROM sys_stat_archiver;
"

echo -e "\n复制槽状态:"
ksql -U $DB_USER -d $DB_NAME -p $DB_PORT -c "
SELECT slot_name, slot_type, active, restart_lsn
FROM sys_replication_slots;
"

echo -e "\n=== 5. 锁等待情况 ==="
ksql -U $DB_USER -d $DB_NAME -p $DB_PORT -c "
SELECT count(*) AS 等待锁会话数 FROM sys_stat_activity WHERE wait_event_type = 'Lock';
"

echo -e "\n=== 排查完成 ==="
unset PGPASSWORD

六、应急处置红线与十大避坑指南

⛔ 6.1 应急操作绝对红线

  1. 禁止 kill -9 数据库主进程:可能导致数据文件损坏,无法启动
  2. 禁止手动删除 sys_wal 下的 WAL 文件:会导致数据库崩溃,数据丢失
  3. 禁止磁盘满后直接重启数据库:可能导致控制文件损坏,加重故障
  4. 禁止业务高峰期执行 VACUUM FULL、重建索引等重操作:加剧业务阻塞

🚨 6.2 十大生产避坑指南

  1. shared_buffers 贪大:设成内存的 50% 以上,最终触发 OOM 宕机,严格按 25%~40% 配置
  2. 复制槽只建不删:备库下线后忘记删复制槽,导致 WAL 暴涨磁盘占满,是最高发故障
  3. 不配置日志轮转:日志文件一直涨,不知不觉占满磁盘
  4. work_mem 设太大:单会话看着不大,几百个连接累加起来直接内存溢出
  5. 统计信息长期不更新:执行计划跑偏,慢 SQL 雪崩打满 CPU
  6. 长事务无人管:一个事务跑一天,WAL 不能回收、表不能清理,拖垮整个库
  7. 磁盘无告警:等到 100% 满了才发现,没有缓冲时间处置
  8. autovacuum 关闭:为了省性能关掉自动清理,最后表膨胀几十倍
  9. 临时文件无监控:大查询瞬间打满磁盘,业务中断
  10. 故障上来就重启:现场全丢,根因永远查不出来,故障反复出现

七、日常运维预防规范

周期 运维动作 目的
每日 磁盘使用率、内存、CPU 巡检;WAL 目录大小检查 提前发现异常
每周 全库 ANALYZE 收集统计信息;慢 SQL 优化复盘 保障执行计划稳定
每月 表膨胀率检查;容量趋势评估;过期日志清理 控制空间增长
每季度 大表 VACUUM FULL 回收空间;参数合理性复盘 深度性能治理

八、全文总结

人大金仓 V9 的内存、CPU、磁盘三大资源故障,看似复杂,实则都有清晰的排查路径和成熟的解决方案。绝大多数生产故障都不是数据库本身的问题,而是参数配置不合理、运维规范缺失、SQL 质量不高导致的。

掌握「自底向上分层排查」的思路,先定位大类原因,再深入到具体 SQL 和参数,就能快速解决问题。更重要的是建立日常预防机制,通过监控告警、定期巡检、SQL 治理,把故障消灭在萌芽状态,这才是生产运维的核心。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐