Oracle 11g 容器故障排查与处置记录
环境标识:某客户服务器 ——
web-svr-01(内网)容器:oracle11g(镜像lhrbest/oracle_11g_ee_lhr_11.2.0.4:1.0)数据库:Oracle 11.2.0.4 EE,SIDORCL,库容量 3.18 GB系统:Kylin Linux Advanced Server V10 (Tercel),内核 4.19.90,Hygon C86 7280 32 核,内存 30 GB处置日期:2026-11-05处置结果:✅ 已恢复并完成容器与参数改造
一、故障现象
连续两天(11-04、11-05)业务系统无法访问,表现为"数据库挂了",人工重启容器后恢复,但找不到原因。
故障时状态:
| 现象 | 说明 |
|---|---|
容器 docker ps | 显示 Up,看不出异常 |
| 应用连接 | 报 TNS-12505: TNS:listener does not currently know of SID |
| 监听器 | lsnrctl status 正常,但实例未注册 |
| 数据库实例 | 已经停止,但容器还活着(这是最容易误判的地方) |
二、故障时间线(从 alert 日志与 audit 日志还原)
11-04 上午 业务侧报告第一次"挂掉"11-04 12:08:51 docker stop oracle11g(SIGTERM → 10 秒未退出 → 强制 SIGKILL,退出码 137)11-04 12:18:08 实例启动,出现 "parallel recovery started with 7 processes"(崩溃恢复)11-04 14:42:35 开始大量 ORA-00600 [spstp: ORACLE_HOME uid does not match euid]11-04 15:00~23:00 每小时 57~72 次,非常稳定11-05 00:00~06:00 每小时 53~55 次,06:00 突增到 107 次11-05 07:19:46 最后一次 ORA-00600(累计 1192 次)11-05 07:21:13 再次 docker stop → 强制 SIGKILL → 07:21:22 实例重启11-05 07:36:06 ⚠️ 同事执行:ALTER SYSTEM SET processes=800 SCOPE=SPFILE; ALTER SYSTEM SET job_queue_processes=20 SCOPE=SPFILE;11-05 11:23 运维排查中执行 shutdown immediate11-05 11:28 强制删除旧容器,实例未关完 → 产生 2 条 ORA-00600 [ksb_shut_detached_process3]11-05 11:29 新容器创建,startup 失败:ORA-00821(SGA 太小)11-05 12:00 用 pfile 覆盖参数后启动成功 → Database opened11-05 12:05 参数写回 spfile(processes=300 / job_queue_processes=100)11-05 12:08 优雅关闭验收通过(>>> 数据库已关闭)11-05 12:15 SGA 扩至 2G、PGA 扩至 512M,重启生效11-05 12:30 磁盘治理:根分区 86% → 61%三、根因分析
3.1 直接原因:参数改错导致实例无法启动
spfile 中:processes = 800 sga_target = 512M启动报错:ORA-00821: Specified value of sga_target 512M is too small, needs to be at least 620M800 个进程所需的 SGA 固定结构(ksl/kss 等)至少需要 620M,而 SGA 只有 512M,实例直接拒绝启动。
后果链条:
实例起不来 → 监听器独立运行(lsnrctl 单独启动的) → 客户端连进来时找不到 SID → TNS-12505 → 应用全部无法连接 → 业务中断 → 但容器主进程是 tail -F,容器仍显示 Up → 运维误判"数据库在跑,只是应用有问题"3.2 为什么"重启就好"
同事 07:36 的 ALTER SYSTEM 是 SCOPE=SPFILE,只写入了参数文件,要重启才生效。之前的每次重启用的都是当时 spfile 里"还没生效"的旧值(processes=300),所以看起来"重启即恢复"。
直到本次真正重启,800 才生效 → 直接起不来。
3.3 历史 ORA-00600(独立问题,已消失)
11-04 14:42 ~ 11-05 07:19 共 1192 次:
ORA-00600: internal error code, arguments: [spstp: ORACLE_HOME uid does not match euid], [500], [0]影响进程:j000(作业从进程)、w000(空间管理)、cjq0(作业队列)、m000(MMON 从进程)性质:实例派生子进程时,ORACLE_HOME 属主 uid(500)与子进程 euid(0)不一致,被 Oracle 拒绝。后果:所有定时作业、空间管理等后台任务全部瘫痪(不影响普通连接)。现状:容器重建后彻底消失,至今零新增(1192 是历史累计值)。
3.4 已排除的原因(避免误判,附证据)
| 怀疑项 | 排除证据 |
|---|---|
| 内存 OOM | OOMKilled=false、RestartCount=0、内核 OOM 记录为空、memory.failcnt=0、容器峰值仅 747 MB |
| 内存不足 | SGA 512M + PGA 78M,30 GB 内存,MemAvailable 长期 15 GB+ |
| 磁盘满 | /data 19%,归档目录不存在堆积 |
| setuid 位导致 | audit 显示 11-04 00:00~14:00 全是 euid=0,但当时数据库完全正常,非触发条件 |
| 外部作业(EXECUTABLE) | dba_scheduler_jobs 全是 PLSQL_BLOCK/STORED_PROCEDURE,dba_scheduler_credentials 为空 |
| 数据库自行崩溃 | alert 日志无 Shutting down instance、无致命 ORA 错误;两次退出均为 signal 15(docker stop 发起) |
3.5 暴露出的容器设计缺陷
原 command 结尾是:
tail -F listener.log alert_ORCL.log # ← 容器主进程是 tail,不是数据库导致:1. 数据库死了容器仍显示 Up → restart: always 完全失效2. docker stop 只杀 tail → 10 秒后 SIGKILL 整个容器 → 每次停库都要崩溃恢复3. hostname 随容器变化 → 重建容器后 tail 找不到日志路径 → 秒退重启循环
四、处置步骤
4.1 应急恢复:用 pfile 覆盖参数启动
# ① 宿主机写 pfile(注意:docker exec 不带 -i 时 heredoc 会丢失,必须在宿主机写)cat > /tmp/init_fix.ora <<'EOF'spfile=/u01/app/oracle/product/11.2.0.4/dbhome_1/dbs/spfileORCL.oraprocesses=300job_queue_processes=100EOF# ② 拷入容器docker cp /tmp/init_fix.ora oracle11g:/tmp/init_fix.oradocker exec oracle11g cat /tmp/init_fix.ora # 必须确认有内容!# ③ 启动docker exec -u oracle oracle11g bash --norc -lc \ "echo 'startup pfile=/tmp/init_fix.ora' | sqlplus -S / as sysdba"结果:
ORACLE instance started.Database mounted.Database opened. ← 服务恢复4.2 参数永久写回 spfile
docker exec -u oracle oracle11g bash --norc -lc "echo 'alter system set processes=300 scope=spfile;alter system set job_queue_processes=100 scope=spfile;exit' | sqlplus -S / as sysdba"⚠️ 多条 SQL 必须换行写,写在一行用
;分隔会报ORA-00911: invalid character。
4.3 容器改造(核心)
新建 /data/oracle11g/entrypoint.sh:
#!/bin/bashexport ORACLE_SID=ORCLterm_handler() { echo ">>> 收到停止信号,正在优雅关闭数据库..." su - oracle -c "export ORACLE_SID=ORCL; sqlplus -S / as sysdba <<SQLshutdown immediateexitSQL" su - oracle -c "lsnrctl stop" 2>/dev/null echo ">>> 数据库已关闭" exit 0}trap term_handler TERM INTsu - oracle -c "export ORACLE_SID=ORCL lsnrctl start sqlplus -S / as sysdba <<SQLstartupexitSQL"tail -F \ /u01/app/oracle/diag/tnslsnr/$(hostname)/listener/trace/listener.log \ /u01/app/oracle/diag/rdbms/orcl/ORCL/trace/alert_ORCL.log &TAIL_PID=$!wait $TAIL_PIDdocker-compose.yml:
version: '3.6' # docker-compose 1.22 最高支持 3.6(写 3.8 会报 unsupported)services: oracle11g: image: registry.cn-hangzhou.aliyuncs.com/lhrbest/oracle_11g_ee_lhr_11.2.0.4:1.0 container_name: oracle11g hostname: oracle11g # 关键:固定主机名,否则重建后 tail 找不到日志 restart: unless-stopped stop_grace_period: 120s # 给数据库 2 分钟优雅关闭 environment: - TZ=Asia/Shanghai - ORACLE_SID=ORCL - NLS_LANG=AMERICAN_AMERICA.AL32UTF8 ports: - "1521:1521" volumes: - /usr/share/zoneinfo:/usr/share/zoneinfo:ro - /etc/localtime:/etc/localtime:ro - /data/oracle11g/app:/u01/app - /data/oracle11g/entrypoint.sh:/entrypoint.sh:ro entrypoint: ["/bin/bash", "/entrypoint.sh"] healthcheck: test: ["CMD-SHELL", "su - oracle -c \"echo 'select 1 from dual;' | sqlplus -S / as sysdba\" | grep -q 1"] interval: 60s timeout: 20s retries: 3 start_period: 300s logging: driver: json-file options: { max-size: "50m", max-file: "3" } networks: - ixjkj_networknetworks: ixjkj_network: external: true切换时注意:旧容器是别的 compose 项目创建的,docker-compose down 会提示 Found orphan containers 且删不掉,必须 docker rm -f oracle11g 再 docker-compose up -d。
4.4 性能参数优化
经评估:库仅 3.18 GB,buffer cache 命中率已达 99.0%(逻辑读 1,221,524 / 物理读 13,081),命中率不是瓶颈,但原 SGA 512M 撑不起多进程且共享池偏小。
| 参数 | 原值 | 调整后 | 说明 |
|---|---|---|---|
sga_max_size | 512M | 2G | 上限,需重启生效 |
sga_target | 512M | 2G | Database Buffers 约 1.44G |
pga_aggregate_target | 78M | 512M | 原值过小,排序会写临时表空间 |
processes | 800(起不来) | 300 | 历史验证可用的值 |
docker exec -u oracle oracle11g bash --norc -lc "echo 'alter system set sga_max_size=2G scope=spfile;alter system set sga_target=2G scope=spfile;alter system set pga_aggregate_target=512M scope=spfile;exit' | sqlplus -S / as sysdba"重启后验证:
Total System Global Area 2,137,886,720 bytes (~2G) Database Buffers 1,509,949,440 bytes (~1.44G) Variable Size 620,758,936 bytes (~590M)4.5 磁盘治理
| 操作 | 释放 | 说明 |
|---|---|---|
swap 从 /swapfile 迁到 /data/swapfile | 8 GB | 原 swap 建在根分区,误占系统盘 |
删除 3 天前的 cron-2026xxxx 归档 | ~2 GB | /var/log 中每天 180M |
# swap 迁移swapoff /swapfilefallocate -l 8G /data/swapfile && chmod 600 /data/swapfilemkswap /data/swapfile && swapon /data/swapfilesed -i '\|^/swapfile |d' /etc/fstabecho '/data/swapfile none swap sw 0 0' >> /etc/fstabrm -f /swapfile# 日志清理find /var/log -maxdepth 1 -name "cron-2*" -mtime +3 -delete结果:根分区 86% → 61%(剩 15 GB)。
4.6 参数基线备份
# spfile 二进制备份docker exec -u oracle oracle11g bash --norc -lc \ 'cp /u01/app/oracle/product/11.2.0.4/dbhome_1/dbs/spfileORCL.ora \ /u01/app/oracle/product/11.2.0.4/dbhome_1/dbs/spfileORCL.ora.baseline.$(date +%F)'# 可读参数清单导出docker exec -u oracle oracle11g bash --norc -lc \ "echo \"create pfile='/tmp/baseline.ora' from spfile;\" | sqlplus -S / as sysdba"docker cp oracle11g:/tmp/baseline.ora /root/oracle_ORCL_baseline_$(date +%F).ora注意:
docker cp在 Docker 18.09 下不支持中文文件名(tar USTAR 编码限制),一律用英文名。
五、改造前后对比
| 项目 | 改造前 | 改造后 |
|---|---|---|
docker stop 停库 | 10 秒后 SIGKILL,每次崩溃恢复 | trap 捕获 → shutdown immediate → 干净关闭 |
| 数据库死了容器状态 | 永远 Up(tail 撑着) | healthcheck 标记 healthy/unhealthy |
| 容器重建 | hostname 变化 → tail 找不到日志 → 秒退循环 | hostname: oracle11g 固定 |
| 容器日志 | 无限制 | 50m × 3 轮转 |
| 自动拉起 | restart: always 形同虚设 | unless-stopped + 可感知健康状态 |
| SGA | 512M | 2G |
| PGA | 78M | 512M |
| swap | 0(后加 8G 在根分区) | 8G,位于 /data |
| 根分区 | 86% | 61% |
六、监控与自动化
6.1 已部署的三个定时任务
*/5 * * * * /root/ora_watch.sh # 数据库无响应时抓现场快照到 /root/ora_incident/*/10 * * * * /root/ora_guard.sh # 数据库无响应时自动重启容器(带优雅关闭)30 * * * * /root/ora_perm_check.sh # bin/oracle 权限被改则自动纠正为 7516.2 日常巡检脚本 /root/ora_check.sh
一条命令输出:容器状态、实例状态、会话/进程数、表空间使用率、归档区、错误计数(累计/新增)、系统资源、自动处置记录。
/root/ora_check.sh关注三点:容器 healthy、实例 OPEN、【6】错误计数新增 = 0。
七、运维红线(必须遵守)
| 禁止 | 正确做法 |
|---|---|
❌ docker-compose down 停数据库 | ✅ docker restart oracle11g(已有 stop_grace_period 保护) |
❌ docker exec -u root ... sqlplus / as sysdba 后 startup | ✅ docker exec -u oracle oracle11g ... 或以 oracle 身份启动 |
❌ 多条 SQL 用 ; 连成一行 | ✅ 每条独立一行,或用 SQL 文件 @/tmp/x.sql |
❌ 手工 chmod/chown 动 $ORACLE_HOME | ✅ 有需求先报备,改完跑 ora_perm_check.sh 复核 |
| ❌ 直接替换 spfile 或用 root 复制 spfile | ✅ 先备份,改完确认属主是 oracle:oinstall、权限 640 |
❌ 单改 processes 不调 SGA | ✅ processes 每 +100 约需 +40M SGA,必须同步调整并重启验证 |
❌ 用 rm 删正在写入的日志 | ✅ 用 : > file 清空(进程持有句柄) |
八、遗留事项
| 事项 | 状态 | 建议 |
|---|---|---|
| 数据库未开归档 | NOARCHIVELOG,无 PITR 能力 | 行业业务建议开启:alter system set db_recovery_file_dest_size=20G; shutdown immediate; startup mount; alter database archivelog; alter database open;(约 2 分钟停机) |
| cron 日志 180M/天 | 会持续占用根分区 | 检查 crond 是否带 -x 调试参数,或把每分钟任务的输出重定向掉 |
| job 203 已删除 | 原 PR_AUTO_DELDATA() 因 log_user=SYS 无法解析而报 ORA-12012 | 若业务需要自动清理,重建时用带 schema 前缀的 APP_SCHEMA.PR_AUTO_DELDATA(); |
| tomcat 每天 01:00 自动重启 | crontab 中有 0 1 * * * systemctl restart tomcat.service | 说明应用侧本身不稳,需单独排查 catalina/应用日志 |
容器无 privileged/shm-size | 当前 SGA 2G 可正常运行 | 若后续继续调大 SGA,需加 shm_size 或 --ipc=host |
九、经验总结
docker ps显示Up不等于数据库活着——容器主进程若是tail/sleep之类的守护命令,数据库死了容器照样 Up。必须配 healthcheck。SCOPE=SPFILE的参数改动是"定时炸弹":改的当下看不出问题,下次重启才爆炸。改完必须评估影响并择机验证重启。- Oracle 参数之间有强关联:
processes与sga_target必须匹配,单改一个很可能起不来(ORA-00821)。 ORA-00821的排查路径:先看alert日志尾部,直接告诉你需要多少 SGA,比猜快得多。docker exec的两个坑:不带-i时 heredoc 内容丢失;不经 shell 时通配符不展开、$VAR不解析。- 停库前先优雅关闭,否则每次都要崩溃恢复;而优雅关闭的前提是容器入口脚本能捕获 SIGTERM。
- 日志、swap、镜像都要"放对分区":swap 8G 建在 40G 的系统盘上,直接把根分区推到 86%。
十、最终状态验收
| 检查项 | 结果 |
|---|---|
| 容器 | Up (healthy) |
| 实例 | OPEN,26 个会话,应用已自动重连 |
| SGA / PGA | 2G / 512M |
| processes | 300(spfile 已固化,可正常重启) |
| 优雅关闭 | ✅ 验收通过(日志出现 >>> 数据库已关闭,无 SIGKILL) |
| ORA-00600 | 零新增(1192 条为历史) |
| TNS-12505 | 0 |
| 根分区 | 61%(剩 15 GB) |
| swap | 8G,位于 /data |
| 自动恢复 | ora_watch(5m) + ora_guard(10m) + perm_check(1h) 已启用 |
| 参数备份 | spfile.baseline + pfile 导出均已保存 |
本文记录一次真实的 Oracle 11g 容器故障排查全过程,涉及 OOM 排除、SGA/processes 关联、容器健康检查改造等实战经验。
评论 (0)