V$SESSION_LONGOPS是定位外部表性能瓶颈的唯一有效数据库视图,因其能捕获外部表扫描进度(OPNAME含“External”),而AWR等待事件和SQL统计均无法反映其真实I/O开销。
AWR里根本看不到外部表的等待事件
外部表(oracle_loader 或 oracle_datapump)的数据读取不经过 oracle 缓冲区缓存,也不触发标准的物理读等待事件(如 db file sequential read 或 direct path read)。因此,在 awr 的 top 5 timed foreground events 中,你永远找不到“external table read”这类事件——它被归入 sql*net message from client 或更隐蔽的 external table i/o 子类,但默认不暴露。
这意味着:单纯扫 AWR 报告的 Top Wait Events 页,对外部表性能问题基本无效。必须换路径。
- 外部表查询在 AWR 的
SQL Statistics页中仍会显示,但Physical Reads和Buffer Gets均为 0 或极低,不能反映真实开销 - 真正耗时藏在操作系统层:文件系统读、字符集转换、行过滤逻辑、预处理器脚本执行等,AWR 不采集这些
- 如果用了
PREPROCESSOR(比如调用 shell 脚本解压或转换),其执行时间完全不计入 SQL Elapsed Time,只体现在客户端感知延迟中
必须结合 V$SESSION_LONGOPS 和 OS 层日志交叉验证
外部表扫描是长操作(long operation),Oracle 会将其注册进 V$SESSION_LONGOPS,只要启用了 DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS(默认启用)。这是唯一能在数据库内捕获其进度的视图。
查法:
SELECT opname, target_desc, sofar, totalwork, units, elapsed_seconds FROM v$session_longops WHERE opname LIKE '%External%' AND sofar <p>关键看 <code>elapsed_seconds</code> 是否远大于同 SQL 在普通表上的执行时间——若相差 3 倍以上,基本确认瓶颈在外围。</p>
-
target_desc显示的是外部表名 + 目录对象名,可快速定位具体对象 -
sofar/totalwork单位取决于访问方式:对文本文件是“行数”,对压缩包可能是“字节块”,需结合units判断 - 若
elapsed_seconds高但sofar == 0,说明卡在文件打开或预处理阶段(如权限失败、shell 超时)
目录对象和文件系统权限是第一排查点
外部表性能差,80% 源于目录对象配置不当或底层文件系统异常,而非 SQL 本身。AWR 不报错,但 V$EXTERNAL_TABLE 和 OS 日志会暴露问题。
- 检查目录对象对应路径是否在数据库服务器本地(不是客户端);远程 NFS/CIFS 共享必须挂载为
noac或hard,intr,否则超时重试会拖慢整条 SQL - 运行
ls -l和stat查目标文件:若Modify时间早于Change时间,说明文件被其他进程锁住(如另一个外部表会话正写同一文件) - 确认 Oracle 用户对目录及文件有
read权限,且 SELinux/AppArmor 未拦截(常见于 RHEL/CentOS;错误表现为 SQL 等待 30 秒后报ORA-29913) - 若用
PREPROCESSOR,检查该脚本是否带调试输出(如echo)、是否依赖环境变量($PATH在 Oracle 进程中通常极简)
字符集与字段分隔符配置不当会导致隐式转换爆炸
外部表定义中的 CHARACTERSET 和 FIELDS TERMINATED BY 若与实际文件不匹配,Oracle 会在每行做逐字节校验和 fallback 解码,CPU 使用率飙升但 AWR 里只体现为高 DB CPU,且 SQL ordered by CPU Time 排名靠前——但这不是 SQL 写得差,是解析器在硬扛。
- 典型症状:AWR 中该 SQL 的
CPU Time占Elapsed Time95% 以上,Buffer Gets和Executions正常,但Rows Processed极低(如每秒仅几百行) - 验证方法:用相同定义建一张小样本外部表(100 行),在 SQL*Plus 中
SET TIMING ON执行SELECT COUNT(*),再对比TKPROF输出中WAIT和CPU时间占比 - 修复方向:确保
CHARACTERSET与文件真实编码一致(用file -i确认);避免用多字节分隔符(如','安全,'|~|'易出错);禁用SKIP和REJECT LIMIT UNLIMITED,它们会触发额外校验
strace -p 跟踪对应 Oracle 进程,看它卡在 open()、read() 还是 wait4()。











