ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Oracle 11g ADR日志定位与adrci清理实践

Oracle 11g ADR日志定位与adrci清理实践

Oracle 11g的日志路径这件事,说起来是个小问题,但我见过太多人在这上面卡住——数据库报了 ORA-00600,人坐在终端前,第一反应是去$ORACLE_HOME/rdbms/log翻,翻半天只看到几个空的或者几天前的文件,然后开始怀疑是不是日志没开。实际上从 10g 到 11g,Oracle 把整套诊断文件的存放方式换了一遍血,admin 那套目录被打散重组,换成了统一的 ADR(Automatic Diagnostic Repository)。你要是还按老地图找新地址,必然找不着。

这篇东西想解决的就是这么一件具体的事:Oracle 11g 的各类日志到底放在哪、怎么用一条 SQL 全查出来、出问题的时候按什么顺序去找、磁盘被撑爆了怎么安全清理、diagnostic_dest要不要动怎么动。不管你是刚接手一套 11g 库的新人,还是从 10g 迁过来还没适应目录结构的老手,看完都能直接上手。我下面讲的所有路径、命令、参数,都是11.2.0.x上实打实跑过的,RAC 和单实例的差异也会点出来。

1. 11g的日志为什么"找不到"了——先把目录结构捋直

1.1 10g那套 admin 目录为什么被整体换掉

10g 时代,日志路径是跟着ORACLE_BASE/admin/<SID>/这个目录走的,bdump、udump、cdump三个子目录各管一摊:后台进程 trace 进 bdump,用户进程 trace 进 udump,core dump 进 cdump。alert log 就躺在 bdump 里,文件名alert_<SID>.log。这套结构直观,但问题也很明显——它和实例是"硬绑"的,路径由ORACLE_BASE加 SID 拼出来,一旦你想把日志挪到单独的挂载点、或者一个机器上跑几十个库,目录就散得到处都是,清理和采集全靠脚本硬编码。

11g 引入 ADR 之后,思路变了:所有诊断数据被当成一个整体来管理,有统一的根目录、统一的生命周期、统一的工具(adrci)来读写。你不再需要记住五个不同的*_dump_dest参数,只需要记住一个diagnostic_dest,剩下的层级由数据库自己按db_unique_name和instance_name生成。这带来的直接后果就是——你在 10g 里形成的路径直觉,在 11g 里全部失效。bdump这个目录名在 11g 里根本不存在了,取而代之的是trace、alert、cdump、incident这几个新名字。

我个人的判断是,这个改动本身是合理的。诊断文件天生就是"按事件、按时间、按实例"三维组织的,10g 的扁平目录撑不住这个模型。但代价是迁移期特别难受,很多老运维的巡检脚本里写死了$ORACLE_BASE/admin/$ORACLE_SID/bdump/alert_$ORACLE_SID.log,上了 11g 之后脚本静默失败,监控看起来一切正常,实际上 alert log 早就不在监控范围里了。这类问题我在接手环境时遇到过不止一次,排查起来极其费劲,因为"没告警"和"没监控到"在表面上完全一样。

1.2 ADR 的树状结构长什么样

把 ADR 想成一棵固定的树,根是diagnostic_dest,默认值就是$ORACLE_BASE。往下每一层都是按固定规则拼出来的,不需要你干预:

<ADR_BASE>/diag/rdbms/<db_unique_name>/<instance_name>/

这里有两个容易搞混的点。db_unique_name不是db_name,虽然大多数单实例环境里两者相同,但只要配过 Data Guard 或者同机建过同名库,db_unique_name就会被改成带后缀的形式,比如ORCL_ST。instance_name也不是 SID 的字符串本身,RAC 环境下每个实例各有一个 ADR Home,orcl1、orcl2分开放,这是好事,排查节点问题时不会互相干扰。

进到 ADR Home 里面,才是真正的文件存放区,固定这么几个子目录:

子目录存放内容排查时的用途
alertXML 格式的 alert log(log.xml)给 ADRCI、EM 读取,不建议手工看
trace文本 alert log、后台进程 trace、用户 trace日常排查的主战场
cdumpcore dump 文件进程崩溃时的内存转储
incident每次严重错误生成的事件包提 SR 时打包上传的原料
hmHealth Monitor 报告手工跑HM.run_report后落盘
incpkg、ir、lck、stage、metadata打包、元数据、锁文件基本不用手工碰

需要重点记住的是:文本格式的 alert log 在trace目录下,不在alert目录下。alert目录里放的是log.xml,那是给工具读的,用tail -f去看它会被一行行的 XML 标签烦死。这个坑我踩过,当时ls到alert目录看到log.xml,以为是新格式的告警日志,打开一看全是<msg>标签,浪费了十几分钟。

1.3 一条 SQL 把关键路径全捞出来

与其背路径,不如直接问数据库。V$DIAG_INFO这个视图是 11g 新增的,里面把当前实例所有诊断相关路径都列出来了,这是我认为最省事、最不容易出错的方式:

SET LINESIZE 200 COL NAME FORMAT A25 COL VALUE FORMAT A90 SELECT NAME, VALUE FROM V$DIAG_INFO ORDER BY NAME;

输出里这几行是需要重点关注的:

  • ADR Base—— ADR 根目录,也就是diagnostic_dest的值
  • ADR Home—— 当前实例的 ADR Home 完整路径
  • Diag Trace—— trace 目录,文本 alert log 就在这里面
  • Diag Alert—— XML 格式 alert log 所在目录
  • Diag Cdump—— core dump 目录
  • Default Trace File——当前会话的 trace 文件名,开了 SQL_TRACE 或 10046 之后直接指向它
  • Health Monitor—— HM 报告目录
  • Active Problem Count/Active Incident Count—— 当前未处理的问题和事件数

Default Trace File这一行我觉得价值被严重低估了。很多人在会话里执行ALTER SESSION SET EVENTS '10046 trace name context forever, level 12'之后,开始满世界找.trc文件,其实一条SELECT VALUE FROM V$DIAG_INFO WHERE NAME='Default Trace File'就解决了,还能避免多会话同时开 trace 时找错文件——这个场景在多个人同时连一个测试库的时候特别常见。

如果数据库还没起来,或者sqlplus都连不上(比如实例卡在 nomount 之前),那就只能靠操作系统层面找了。这时候find命令配合时间排序最实用:

# 在 ORACLE_BASE 下按修改时间找最近的 alert log find $ORACLE_BASE/diag/rdbms -name 'alert_*.log' -mtime -1 -ls # 找最近一小时内被写过的 trc 文件,按时间倒序 find $ORACLE_BASE/diag/rdbms -name '*.trc' -mmin -60 -printf '%T@ %p\n' | sort -rn | head -20

注意:find的时候千万别在/根目录下跑,日志目录分散在ORACLE_BASE和ORACLE_HOME,范围收窄到这两个目录就够了,全盘扫一次在 IO 紧张的库上可能直接把业务拖慢。

2. 六类日志文件的真实位置与具体用途

2.1 alert log:文本和XML两套并存

11g 的 alert log 是双份的,这个设计初看冗余,其实是过渡期的产物。文本版在<ADR_HOME>/trace/alert_<SID>.log,XML 版在<ADR_HOME>/alert/log.xml,两份内容同源,写文本的同时写 XML。对 DBA 来说,只看文本版就够了,XML 版交给adrci或者 EM 去解析。

文本 alert log 里记录的典型内容包括:实例启动关闭的时间戳和参数、日志切换、检查点完成情况、表空间告警、ORA- 错误、归档失败、以及各种内部诊断信息。我排查问题的习惯是先tail -200看最近发生了什么,再按时间点grep关键字。几个高频用法:

# 实时盯 alert log tail -f $ORACLE_BASE/diag/rdbms/orcl/orcl/trace/alert_orcl.log # 只看错误行,带前后文 grep -n -A3 -B3 'ORA-' alert_orcl.log | tail -100 # 统计最近一天各类错误出现次数 grep -o 'ORA-[0-9]\{5\}' alert_orcl.log | sort | uniq -c | sort -rn

有一个细节值得说:alert log 在 11g 里是被"截断复用"的,不是无限增长的。当文件超过一定大小(大约 10MB 量级),数据库会把它改名成alert_<SID>.log.1之类的备份,然后重新开始写。所以如果你在 alert log 里只看到最近几天的内容,别慌,不是日志丢了,是滚动掉了。想找更早的,去同目录下找带数字后缀的文件。这个机制比 10g 好,10g 是直接无限追加的,跑个几年下来 alert log 能到几个 G。

另外,11g 里还保留着background_dump_dest、user_dump_dest、core_dump_dest这三个参数,你用show parameter dump_dest还能查出来值。但它们的值会被自动指向 ADR 下对应的目录(background_dump_dest指向trace,core_dump_dest指向cdump),而且如果你手工显式设置它们,数据库会忽略并给出警告。所以正确做法是一个都别设,让diagnostic_dest统一管。这点在老库升级的时候特别容易出问题——spfile 里带着 10g 时代留下的background_dump_dest设置,升到 11g 后启动时报一堆警告,路径还和实际不一致。

2.2 trace 文件:后台进程和用户进程不是一回事

trace目录下的.trc文件分两类,命名规则不同,用途也完全不同。

后台进程 trace,文件名形如orcl_smon_12345.trc,格式是<SID>_<进程名>_<PID>.trc。这类文件由 SMON、PMON、DBWn、LGWR、CKPT、MMON 等后台进程产生,平时大部分是空的或只有几行启动信息,只在出问题时才有内容。比如 SMON 做实例恢复、DBWn 卡住、MMON 收集 AWR 快照失败,都会往这里写。排查时按进程名找最方便:

ls -ltr $ORACLE_BASE/diag/rdbms/orcl/orcl/trace/*_dbw*.trc ls -ltr $ORACLE_BASE/diag/rdbms/orcl/orcl/trace/*_mmon*.trc

用户进程 trace,文件名形如orcl_ora_23456.trc,格式是<SID>_ora_<PID>.trc。这类文件由你在会话里手工开启的 SQL_TRACE、10046 事件、10053 优化器跟踪产生,也可能是某个会话报错(比如 ORA-00600)时自动生成的。找这类文件的正确姿势是从V$DIAG_INFO拿Default Trace File,或者从V$SESSION关联V$PROCESS拿 SPID,再拼出文件名。

trace 文件的大小由MAX_DUMP_FILE_SIZE控制,默认值在不同版本里不太一样,有UNLIMITED的,也有按块数给的。我的建议是明确设一个上限,比如ALTER SYSTEM SET max_dump_file_size = '200M'。理由很直接:一个开了 10046 level 12 的大并发会话,trace 文件几分钟就能把挂载点写满,而且写满之后影响的不只是这个会话,整个库的归档、写 redo 都可能跟着报错。这个参数的取值建议结合挂载点剩余空间来定,200M 到 500M 是比较稳妥的区间。

还有个小技巧,trace目录下会有个alert_<SID>.log和一堆.trc,有些人清理的时候图省事rm *.trc一把梭。别这么干。正在运行的进程持有的 trace 文件被删掉之后,文件句柄不会释放,磁盘空间不会立刻回收,lsof | grep deleted能看到一堆幽灵文件,直到进程重启才真正释放。清理要走adrci,这个后面细说。

2.3 监听日志:最容易"搬家"的那一个

监听器的日志位置在 11g 里是个高频困惑点,因为它和数据库的 ADR 是两套独立的根目录,而且实际路径取决于启动监听时环境变量里的ORACLE_BASE是什么。

标准位置是:

$ORACLE_BASE/diag/tnslsnr/<主机名>/listener/trace/listener.log

注意这里中间那层是主机名,不是实例名。同一台机器上如果有多个监听(比如 Oracle Restart 环境下有个LISTENER和一个LISTENER_ASM),各自有一个目录。

最靠谱的确认方式是直接问监听器自己:

lsnrctl status

输出里会有明确的一行Listener Log File,直接告诉你路径。这个命令比猜路径可靠一百倍,尤其是集群环境里监听由 grid 用户启动、ORACLE_BASE和数据库用户不是同一个的时候,路径会落在 grid 的 base 下面,你按 oracle 用户的$ORACLE_BASE去找,永远找不到。

还有一个常见现象:listener.log涨得飞快,一天几个 G。原因通常是客户端连接风暴、连接池配置不当、或者有人在扫端口。11g 里监听日志没有自动滚动机制,会一直往同一个文件里写。这时候不能直接rm,因为监听器进程还开着这个句柄。正确做法是先停写再处理:

lsnrctl set log_status off mv listener.log listener.log.bak_$(date +%Y%m%d) lsnrctl set log_status on

mv之后句柄仍然指向原 inode,但新写入会走到新文件,所以顺序上必须先set log_status off再mv,不然中间会丢日志。这个操作我在生产上做过多次,很稳。如果确实不需要监听日志,可以长期关掉,但对排查连接问题的帮助很大,建议保留,只是定期处理。

2.4 归档、redo、控制文件、审计文件

严格说这几类不算"日志路径",但排查问题时经常和日志一起用,顺手列清楚,省得再查一遍视图。

归档日志的位置由log_archive_dest_n决定,用一条 SQL 就能看到:

SELECT DEST_ID, STATUS, DESTINATION, ERROR FROM V$ARCHIVE_DEST WHERE STATUS <> 'INACTIVE'; SHOW PARAMETER log_archive_dest

11g 里db_recovery_file_dest(FRA)用得也很多,归档落在 FRA 下面时,实际路径是<FRA>/<db_unique_name>/archivelog/<日期目录>/。FRA 的空间问题比路径问题更常见,V$RECOVERY_FILE_DEST和V$FLASH_RECOVERY_AREA_USAGE能看清占用分布。

redo log和控制文件的路径直接查视图:

SELECT GROUP#, MEMBER, STATUS FROM V$LOGFILE ORDER BY GROUP#; SELECT NAME FROM V$CONTROLFILE; SELECT NAME FROM V$DATAFILE;

审计文件是另一个容易踩坑的点。11g 的audit_file_dest默认值还是 10g 那套$ORACLE_BASE/admin/<SID>/adump,没有跟着 ADR 走。所以你会看到一个很奇怪的现象:诊断文件全在diag/rdbms/下面,审计文件却还留在admin/下面。这不是配置错了,是设计如此。查一下确认:

SHOW PARAMETER audit_file_dest SELECT VALUE FROM V$PARAMETER WHERE NAME = 'audit_file_dest';

adump目录下的文件分两种,.aud是审计记录,ora_<PID>.aud是会话审计;另外还有一种不带扩展名的ora_<PID>.aud临时文件,实例异常终止时容易残留。这些残留文件不会自动清理,日积月累能占不少空间,定期扫一下是必要的。

3. 实战流程:定位、查看、打包、清理

3.1 三步定位法,先从视图问起

拿到一台陌生的 11g 库,我一般按这个顺序走,基本不会走弯路。

第一步,确认能连上就用视图。V$DIAG_INFO一条 SQL 解决所有路径问题,前面已经写过。这一步能拿到ADR Base和ADR Home两个关键值,后面所有路径都基于它们拼。

第二步,连不上就用环境变量加 find。登录主机,su - oracle,然后:

echo $ORACLE_BASE echo $ORACLE_HOME echo $ORACLE_SID ps -ef | grep pmon | grep -v grep

从 pmon 进程能看出当前跑了哪些实例。拿到 SID 之后:

find $ORACLE_BASE/diag/rdbms -maxdepth 3 -type d -name "$ORACLE_SID"

这一步能定位到 ADR Home。如果ORACLE_BASE是空的(有些环境下变量没配进 profile),从 pmon 进程的/proc/<pid>/environ里也能挖出来:

tr '\0' '\n' < /proc/$(pgrep -f "ora_pmon_" | head -1)/environ | grep ORACLE

这招在接手别人维护的机器时特别管用,能还原出实例启动时的真实环境,避免被当前 shell 里的变量误导。

第三步,确认权限和属主。ADR 目录的属主应该是启动实例的那个用户,权限一般是750或755。如果你用 root 去ls,能看到文件但tail不了,那就是权限问题。切到正确的用户再操作,别用 root 去chown或者chmod,改错了会让数据库下次启动写日志失败。这个坑很隐蔽,因为 root 权限下一切看起来都正常,直到数据库重启报错才发现。

3.2 adrci 实操命令清单

adrci是 11g 配套的诊断工具,很多人装了库但从来没用过,其实它比手工ls加tail高效得多。进入方式很简单,在 shell 里直接敲adrci:

$ adrci adrci> show homes

show homes会列出当前 ADR Base 下所有的 ADR Home,多实例机器上能一次看全。如果只看到一个或者不对,用set basepath指定根目录,用set homepath指定具体 home:

adrci> set basepath /u01/app/oracle adrci> set homepath diag/rdbms/orcl/orcl

注意set homepath后面跟的是相对路径,相对于 basepath,不是绝对路径。这个细节文档里写得不显眼,我第一次用的时候直接给了绝对路径,报错提示也不够明确,试了好几次才反应过来。

几个最常用的命令,我基本是这五条走天下:

命令作用使用场景
show alert -tail 100看 alert log 最后100行快速看最近发生了什么
show alert -p "message_text like '%ORA-00600%'"按条件过滤 alert定位特定错误
show problem列出未关闭的问题判断有没有遗留故障
show incident列出事件明细关联具体错误和 trace
ips pack incident <id> in /tmp打包事件包提工单时上传分析

show alert有个比tail -f强的地方:它支持时间范围过滤和关键字过滤,而且是解析过的结构化输出,不会把一条跨行的错误截断。比如查某天上午的错误:

adrci> show alert -p "originating_timestamp > '2024-05-20 08:00:00' and originating_timestamp < '2024-05-20 12:00:00'"

这个用法在处理"昨天半夜发生过一次报错,但现在日志已经滚过去了"这类问题时特别有效。

3.3 清理策略与容量估算

ADR 目录的清理是大头,尤其是trace和incident这两个子目录。我的原则是能用adrci purge就不用rm,理由是 ADR 有一套元数据在记录文件和事件的对应关系,直接删文件会让元数据指向不存在的文件,show incident会出现一堆查不到详情的空记录。

adrci的清理命令有两个维度,按时间和按大小:

adrci> purge -age 10080 -type trace adrci> purge -age 4320 -type cdump

-age的单位是分钟,10080 分钟是 7 天,4320 分钟是 3 天。-type可以指定ALERT、TRACE、CDUMP、INCIDENT、ALL。另外还有一个按总量控制的-size参数,但实际用下来-age更可控。

容量规划上,我一般按这个粗算方式给建议:

项目单日增量(中等负载)建议保留占用估算
alert log(含滚动备份)20~100 MB30 天0.6~3 GB
后台 trace50~200 MB7 天0.35~1.4 GB
用户 trace(有 10046 时)200 MB~2 GB3 天0.6~6 GB
incident 包不定,故障期暴涨30 天视故障频率
listener.log100 MB~5 GB按需差异极大

这个表只能作参考,实际增量跟业务量、报错频率、有没有人开着 trace 跑长事务强相关。我见过一个库trace目录一天涨了 15G,最后查出来是某个定时任务每次都开 10046 但从不关闭,一天跑两百多次。所以清理只管一时,真正该做的是把异常源头掐掉,不然清完还会长回来。

另外要特别注意incident目录。正常时期它是空的,一旦出现 ORA-00600、ORA-07445 这类内部错误,每次会自动打包一个事件目录,里面装着 trace、core、状态转储,单个包几十兆到几个 G 都正常。故障密集期这个目录能迅速吃光挂载点,而且这些包不能随便删,是分析根因的唯一线索。合理做法是定期ips pack压缩归档到别的存储,再从 ADR 里 purge 掉。

4. diagnostic_dest 怎么规划、怎么改、改完怎么验

4.1 目录规划:别跟着 ORACLE_BASE 挤在系统盘

diagnostic_dest默认取ORACLE_BASE,而ORACLE_BASE在很多标准安装里是/u01/app/oracle,跟在软件安装盘上。这个默认值在测试环境无所谓,生产环境有风险:日志把软件盘写满,影响的不只是诊断,可能连ORACLE_HOME下的临时文件都写不了,问题会瞬间放大。

我的建议是把诊断目录单独挂一个盘,或者至少和软件、数据目录分开,容量按上面那张表算完之后再乘以 2 的余量。规划时还有两个考虑:

一是多实例共用一个根。diagnostic_dest指向同一个目录,下面自动按rdbms/<db_unique_name>/<sid>分层,不会冲突。这样管理的盘只需要监控一个挂载点,巡检脚本也简单。

二是权限统一。目录属主设成oinstall组,权限775,所有可能启动实例的用户(oracle、grid 等)都在这个组里,避免出现 A 用户启动的实例、B 用户读不了日志的尴尬。RAC 环境下如果共享存储,路径在多节点上要一致,否则adrci在不同节点看到的内容不一样,排查时会怀疑人生。

4.2 修改流程:改 spfile 加重启,别想着在线生效

diagnostic_dest是个静态参数,性质上接近基本参数,在线改不了。你执行ALTER SYSTEM SET diagnostic_dest = '...' SCOPE = BOTH大概率会报ORA-02095之类的错误,或者虽然执行成功但实际值没变。正确流程是:

-- 1. 先看当前值 SHOW PARAMETER diagnostic_dest -- 2. 只改 spfile ALTER SYSTEM SET diagnostic_dest = '/u02/oradiag' SCOPE = SPFILE; -- 3. 正常关闭并启动 SHUTDOWN IMMEDIATE; STARTUP; -- 4. 验证 SELECT NAME, VALUE FROM V$DIAG_INFO WHERE NAME IN ('ADR Base','ADR Home','Diag Trace'); SHOW PARAMETER diagnostic_dest

改完之后有两件事必须确认。第一,目录是不是自动创建了。11g 启动时如果目标目录不存在,通常会尝试创建,但如果上层目录的权限不对,会创建失败,导致实例启动时报 ADR 相关错误甚至起不来。保险做法是重启前手工把目录建好并授权:

mkdir -p /u02/oradiag/diag/rdbms chown -R oracle:oinstall /u02/oradiag chmod -R 775 /u02/oradiag

第二,老的目录要不要迁。已经产生的历史 trace 和 incident 不会自动搬过去,新的日志从重启那一刻开始写在新目录。如果历史日志还有分析价值,用cp -a拷过去,保持 ADR Home 的相对结构,adrci就能识别。如果不迁,至少保留一段时间再清理,别重启完就删——真出事的时候,重启前那段时间的日志往往最关键。

提示:生产环境改这个参数一定要走变更窗口。虽然只是改个路径加重启,但重启期间业务中断,而且万一新路径权限有问题导致实例起不来,回滚成本会很高。改之前把旧值记下来,回滚就是把参数改回去再启动一次。

4.3 与旧参数的优先级关系,以及 RAC 的注意点

前面提过,background_dump_dest、user_dump_dest、core_dump_dest这三个参数在 11g 里被弱化了。准确的规则是:只要设了diagnostic_dest(它有默认值,所以基本等于永远设着),这三个参数就会被忽略,值被强制指向 ADR 对应子目录。你显式设置它们,启动时 alert log 里会出现类似"参数已废弃"的提示,值也不会生效。

所以从 10g 升级上来的库,第一件事应该是清理 spfile 里残留的这三个参数:

ALTER SYSTEM RESET background_dump_dest SCOPE = SPFILE; ALTER SYSTEM RESET user_dump_dest SCOPE = SPFILE; ALTER SYSTEM RESET core_dump_dest SCOPE = SPFILE;

不清理的后果是监控脚本和实际路径不一致,排查时看到show parameter输出的值和真实文件位置对不上,很容易误判。

RAC 环境还有两个额外注意点。一是每个实例都要改,diagnostic_dest在 RAC 里通常按实例设置(虽然 spfile 共享,但可以按 sid 区分),改的时候用ALTER SYSTEM SET ... SID = 'orcl1' SCOPE = SPFILE,两个节点分别来一遍。二是切换节点时留意 ASM 实例的 ADR,ASM 有自己的 ADR Home,路径是diag/asm/+asm/<asm_instance>,和数据库的分开,别只改数据库的把 ASM 的漏了,ASM 出问题时同样需要看日志。

5. 常见问题速查与踩坑记录

5.1 症状对照表:先看现象再去动手

排查这类问题,最怕的是没有方向感。我整理了一张对照表,基本覆盖了我遇到过的所有情况,看到现象直接跳到对应动作,能省不少时间。

现象最可能的原因处理动作
找不到 alert log还按 10g 的 bdump 路径找查V$DIAG_INFO的Diag Trace
show parameter dump_dest有值但目录里没东西值被 ADR 覆盖了,实际在别处以V$DIAG_INFO为准,忽略旧参数
alert log 只剩最近几天达到大小上限自动滚动找同目录下带数字后缀的备份文件
listener.log 暴涨连接风暴或端口扫描先set log_status off,再mv
alert log 里没有报错但业务确实异常看的是 XML 版或者错误的实例目录确认ADR Home是不是当前实例
trace 文件删了但空间没释放进程持有句柄lsof | grep deleted,重启进程才真正释放
磁盘被incident撑满故障期自动打包的事件目录ips pack归档后purge,别直接rm
adrci进去只看到一个 homebasepath指向不对set basepath到正确的ORACLE_BASE
重启后实例起不来,报 ADR 错误新路径不存在或权限不对检查属主权限,改回旧值先启动
用户 trace 文件找不到没开 trace 或找错了 PID查V$DIAG_INFO.Default Trace File

这张表里有一条我想单独展开:"alert log 里没有报错但业务确实异常"。这种情况我遇到过几次,最后发现都是看错了实例的目录。一台机器上跑了三个实例,diag/rdbms/下面三个 Home,人凭印象进了其中一个,tail半天没动静,还以为日志没开。养成习惯,每次进目录前先pwd确认,或者干脆用adrci的show homes加set homepath显式切换,比靠cd走稳。

5.2 几条用血换来的经验

第一,别用rm清 trace。这个前面说过,但值得再强调一次。除了句柄不释放的问题,还有一个后果是 ADR 元数据不一致。adrci的show incident依赖元数据和文件的对应关系,文件被删了之后你会看到一堆"找不到文件"的条目,真出问题时反而干扰判断。统一用purge,哪怕多敲几个字。

第二,把路径查询写成脚本固化下来。我维护环境时会在每个库上放一个get_diag_path.sql,内容就是查V$DIAG_INFO,外加几个SHOW PARAMETER。巡检、交接、排查第一步都跑它,输出的结果贴到工单里,谁看都清楚。这个习惯省掉了很多"你说的路径和我看到的不一样"的扯皮。

第三,注意时间对齐。alert log 里的时间戳是数据库所在主机的本地时间,而ls -l显示的文件时间也是本地时间,看起来一致,但如果应用服务器和数据库服务器时区不同,就会出现"应用报错时间是 10:00,日志里找不到 10:00 的记录"。排查跨时区系统时先确认时区,SELECT SYSDATE, CURRENT_TIMESTAMP, DBTIMEZONE, SESSIONTIMEZONE FROM DUAL一眼看清,比事后分析半天强。

第四,log.xml不要用tail看。它是一条记录一行还是一行记录多行取决于格式,tail出来的内容经常是半截的,用adrci show alert或者xmllint --format格式化之后再看。

第五,定期检查而不是出事才查。我见过太多环境是磁盘写到 99% 才有人发现。加一条简单的监控,每天凌晨跑一次采集,超过阈值就告警,成本极低,收益很大:

#!/bin/bash THRESHOLD=80 ADR_BASE=$(su - oracle -c "sqlplus -S / as sysdba <<EOF set head off pages 0 feed off select value from v\\\$diag_info where name='ADR Base'; exit EOF" | tr -d ' ') USAGE=$(df -h "$ADR_BASE" | awk 'NR==2 {gsub(/%/,"",$5); print $5}') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "ADR 所在挂载点使用率 ${USAGE}%,请检查 $(du -sh $ADR_BASE/diag/* | sort -rh | head -5)" fi

脚本本身很粗糙,但胜在能跑、能告警。真正落地的时候再按自己的监控体系改造成对接统一告警平台。

我个人在实际操作中的体会是,11g 这套 ADR 结构刚上手确实别扭,因为打破了过去十年形成的路径直觉。但用熟之后不得不承认它更合理——一个根目录、一套工具、按实例自动分层,清理和打包都有统一入口。真正需要记住的其实就三件事:V$DIAG_INFO查路径、adrci看和清、diagnostic_dest定根。剩下的细节,出问题时现查都来得及。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进