资讯详情

Oracle SQLT慢SQL诊断实战:从归档包到执行计划分析

📅 2026/10/9 13:53:02 | 华诺云谱 👁 阅读
Oracle SQLT慢SQL诊断实战:从归档包到执行计划分析
简介SQLTSQL Tuning Advisor Test是Oracle官方常用于SQL性能调优的辅助工具包这份资源面向DBA、数据库管理员及需要优化SQL执行计划的开发者覆盖10g、11g、12c、18c、19c等多个版本适应不同版本下的调优场景。包内共205个文件以160个sql诊断脚本为主体同时包含19个pkb和19个pks包源码、5个txt说明及2个html报告页面整个压缩包仅927KB轻量且便于分发部署。目前已有419人学习下载适合有一定SQL调优基础的中高级数据库从业者。内容除了用于绑定或迁移SQL Profile的脚本还提供pkb/pks包体源码并附有变更说明与操作指引可辅助生成性能分析报告、对比不同版本下的执行计划或作为日常巡检的工具参考。对需要快速定位慢SQL、稳定执行计划的DBA和开发人员而言这套脚本具备较高的实用价值既可在测试环境验证也可直接用于生产环境的排查与优化。1. 看到这个文件名别急着删SQLT 归档包是 Oracle 慢 SQL 诊断的后悔药sqlt_10g_11g_12c_18c_19c_5th_June_2020.zip这个文件名一眼看过去像某个没人维护的临时打包不少 DBA 收到它时甚至会先怀疑是不是发错了。它其实是 Oracle 的 SQLTSQLTXPLAIN在 2020 年 6 月 5 日发布的一份归档快照版本覆盖从 10g 到 19c 的全线数据库。它的核心价值不是帮你把 SQL 改好而是在你还没想清楚问题在哪时先把一条慢 SQL 周围的所有诊断证据完整保存下来包括执行计划、绑定变量、表统计信息、优化器参数、是否被 SQL Profile 固定。适合刚接手业务库、遇到一条“突然变慢”的 SQL 时先把现场封好再做下一步分析而不是等 SQL 被刷出 shared pool 后追悔莫及。2. SQLT 到底打包了什么先看清这套黑匣子的结构与产物2.1 它不是单个脚本而是一整套 PL/SQL 包加 SQL*Plus 包装层第一次解压这个 zip 的人通常会愣一下里面不是一两个文件而是分成 install、utl、doc 等多个目录的一组脚本。常见做法里运行入口是sqltxtract.sql系列脚本真正的处理逻辑放在安装时生成到数据库里的SQLTXPLAINschema 中。这意味着 SQLT 不是一个运行时解释执行的命令行工具而是先把一堆 PL/SQL 包体、表、视图装进目标库再由包装脚本去调用。这套设计的用意在于它需要访问的数据源分布在 V$ 动态性能视图、DBA 字典、AWR 快照、甚至外部表文件里。如果每次运行都临时解析权限和依赖关系会非常脆弱。提前安装成 schema 后后续每次抓取只是按参数组装查询并输出报告重复执行的成本很低。我在生产上第一次用的时候以为它只是包装了 DBMS_XPLAN后来翻脚本才知道它把 DBMS_SQLTUNE、DBMS_STATS、DBMS_SQL 全部调动起来一次性采集的数据面比我手工查 20 条 SQL 还要广。它可以抓你当前库里的 SQL 实时信息也可以从 AWR 历史快照反查一个已经不再缓存的旧 SQL这一点正是慢 SQL 诊断最需要的能力。很多人只把 SQLT 当成“执行计划查看工具”其实它是完整取证工具这也是它会被称为黑匣子的原因你不需要完全理解内部每一步它会把散落在多个数据源里的证据归拢到固定目录形成一份可复读的诊断档案。2.2 运行后到底产出什么主报告与配套文件的阅读顺序SQLT 完成一次抓取后会按 SQL ID 为维度生成一组文件。常见布局是一个主 HTML 报告文件名通常类似sqlt_sql_id_hash_main.html旁边配着纯文本版的执行计划、采集到的绑定变量列表、表统计信息摘要以及一个可以被手动执行的 profile 载入脚本。不要一上来就翻 HTMLHTML 报告把最关键的结论放在前面但细节验证都要回到文本文件里。阅读顺序建议固定下来先看主报告的摘要段确认这次抓到的 SQL 文本、SQL ID、子游标数量、是否命中 SQL Profile再去看执行计划部分重点关注优化器估算出来的 rows 和实际执行产生的 rows 是否严重偏差最后回到纯文本辅助文件核对绑定变量快照和表统计信息是否过期。这样一套走下来比漫无目的地在文件堆里乱翻要快得多。主报告和辅助文件的分工可以这样对照产物类型作用什么时候必须看主 HTML 报告汇总执行计划、成本、命中 profile 情况对问题建立第一印象执行计划文本文件记录 plan 的完整步骤与谓词信息需要核对具体算子时绑定变量文件保存本次抓取时的变量值和数据类型怀疑绑定变量偷窥时profile 载入脚本可手动执行的优化建议脚本确认问题原因后想复现验证统计信息摘要表和索引的行数、直方图信息判断优化器是不是基于过期统计信息读报告时会发现 SQLT 提供了很多“疑似原因”的标注比如某个直方图不存在、某些表缺少统计信息、SQL 使用了非默认的 optimizer 参数。但要注意这些标注是提示不是结论最后定责仍然要结合业务场景判断。2.3 SQLT 的边界在哪它不负责什么和很多 DBA 的直觉相反SQLT 本身不修改你的 SQL。它最多给出一个 SQL Profile 的建议脚本要不要采纳、要不要在确认安全后应用都由人决定。它也不负责应用层面的改造不判断这条 SQL 的写法是否和业务逻辑冲突更不替代 AWR 做整个实例的性能趋势分析。它最擅长的是单条 SQL 的定点取证。迁移前后执行计划漂移、夜批任务突然变慢、同一 SQL 在 10g 与 19c 上表现迥异这类问题用 SQLT 是最舒服的。如果你要解决的是整个库的负载均衡、IO 带宽不足、大量并发锁竞争AWR 报告和等待事件分析更合适不是一个工具包能覆盖的。你从标题里的10g_11g_12c_18c_19c也能看出它的定位它服务的是跨版本环境尤其是从老版本迁移到新版本后SQL 行为不兼容这类复合问题。黑匣子能给你完整的原始证据但最终解释权始终在你手里。3. 在 19c 上装 SQLT选版本、看 CDB/PDB、跑安装脚本的最小落地路径3.1 2020 年 6 月 5 日这个归档版本到底该怎么选型文件名里的5th_June_2020是归档日期不是数据库版本。拿到这个包之后先不要急着装我一般会先确认目标库的情况再决定是否直接用这个版本。第一步检查数据库版本和补丁级别第二步确认是单实例还是 RAC第三步确认是不是容器数据库架构第四步确认当前连接进去的是 CDB 根还是 PDB。命令层面没有特殊技巧进 SQL*Plus 看一眼就行sqlplus -S / as sysdba SQL select banner from v$version where rownum 3; SQL select name, cdb from v$database; SQL show con_name;这段命令的逻辑很简单v$version给出数据库的大版本和小补丁信息v$database的 cdb 字段告诉你这个库是不是容器库show con_name告诉你当前会话落在哪个容器里。SQLT 对 12c 之后的容器库支持是有讲究的它不是装一次就全局可用的工具后面会在避坑章节专门展开。如果目标库是 19c 且补丁比较新这个 2020 年的归档通常可以正常安装和使用。SQLT 这类工具对数据库小版本并不敏感真正影响它的是优化器参数、DBMS_SQLTUNE 的接口变化。如果你在 19c 上遇到报错第一反应不应该是换一个更新版本来“碰运气”而是先看报错是不是发生在调用sys.dbms_sqltune时权限不足。3.2 安装前置目录、环境变量和 SYSDBA 权限SQLT 安装时的常见做法是把它放在数据库软件目录之外例如/opt/sqlt或/u01/tools/sqlt。不要放进$ORACLE_HOME里因为 Oracle 补丁升级时很可能会清掉或覆盖外来脚本装完的包会莫名消失这是很多 DBA 踩过的坑。安装必须使用具备 SYSDBA 权限的账号因为脚本要在数据库里创建用户和授权。它默认会创建一个叫SQLTXPLAIN的 schema授予访问动态性能视图和执行 dbms 包的必要权限。这里要提醒有些库出于安全考虑关闭了UTL_FILE相关权限或限制了目录访问SQLT 需要写外部文件安装前要确认数据库的utl_file_dir或者它能直接使用默认目录否则运行时会在写文件环节报权限错误。解压后建议手动设置环境变量指向解压目录。每个包对环境变量的命名不完全一样比较稳定的做法是在运行前cd到解压根目录再把当前目录作为基准路径传给脚本而不是在任意目录下用绝对路径调用内部子脚本。3.3 以 SYSDBA 执行安装一条命令和它背后的动作准备就绪后实际安装命令很短cd /opt/sqlt sqlplus / as sysdba SQL install/sqltinstall.sql安装脚本会先检查当前连接环境然后依次创建SQLTXPLAIN用户、基础表、PL/SQL 包体并在结束时输出安装成功的提示。如果需要调整默认表空间可以在执行安装前先为SQLTXPLAIN用户准备一个专门的表空间避免它把对象塞进SYSTEM。我一般习惯把它指向业务库的SYSAUX或者手动建一个小表空间这样日后的碎片整理和权限回收都有清晰边界。执行安装时脚本会收集一些当前库的参数信息作为后续诊断的基线。这意味着安装过程本身会对数据库产生少量短暂负载建议在维护窗口或者业务低峰期进行不要卡在业务高峰时段。RAC 环境下SQLT 安装通常只需要在一个节点执行因为对象是保存在共享存储的数据库字典里的但运行时涉及实例级数据时要看你连接的是哪个实例。安装完成后可以用一条简单的查询验证对象是否完整创建SQL select count(*) from dba_objects where owner SQLTXPLAIN;如果返回的对象数量过少或者执行脚本时报PLS-00201这类标识符错误多半是安装过程中部分包体没有编译成功。以 19c 为例常见原因是数据库里存在无效对象或者DBMS_SQLTUNE被某个安全策略锁定。3.4 快速验证不抓 SQL先确认工具本身能跑安装成功只是第一步我更习惯接着做一次最轻量的自检不分析任何业务 SQL直接用一条最简单的查询作为输入跑一遍sqltxtract.sql的交互入口让它抓取当前会话刚刚执行过的那条 SQL。这么做的好处是把“工具运行环境问题”和“具体 SQL 分析问题”分开免得后面出了状况分不清是 SQLT 坏了还是 SQL 本身诡异。自检过程中如果一切正常输出目录会多出一组以sqlt_开头的文件。打开主报告看一眼执行计划是否完整、绑定变量是否为空数组基本就能确认装好的 SQLT 可以投入使用了。我在每次升级数据库补丁后也会这样做一次自检就是怕 Oracle 的某些内部接口变化导致脚本失效自检的成本比临时抓真实 SQL 低得多。4. 用 SQLT 抓一个真实慢 SQL交互式抓取、静默模式与控制收集范围的开关4.1 按 SQL ID 抓取先确认目标再走交互流程面对一条正在变慢的 SQL最稳妥的做法是先从v$sql里确认它的 SQL ID再让 SQLT 按 SQL ID 抓取。这样做数据链完整会连带抓出子游标数量、执行次数、平均耗时等运行时信息。SQL select sql_id, sql_text, elapsed_time, executions from v$sql where sql_text like %你的表名% order by elapsed_time desc;拿到 SQL ID 后进入 SQLT 交互入口SQL sqltxtract.sql交互过程会依次询问 SQL ID、是否采集执行计划、是否附带统计信息、是否生成 profile 建议等。每一步都有默认值如果拿不准全部回车保持默认先拿到一份完整报告再说。第一次跑不要激进地关掉任何采集项数据全一点后面分析才有对照。4.2 静默模式把参数一次性传进去适合批量抓取交互模式适合单条突击分析但如果要抓一批 SQL每次都等提示输入会把人逼疯。SQLT 的入口脚本支持在调用时把关键参数串在脚本名后面用空字符串表示“使用默认值”。常见的传参顺序是 SQL ID、抓取模式、是否收集 plan、是否收集统计信息、是否生成 profile 脚本等具体顺序以你这个包里的脚本头部注释为准。示例sqlplus -S / as sysdba EOF /opt/sqlt/sqltxtract.sql 5g1x2m3p4q N Y EOF这段命令通过 Here Document 把脚本喂给 SQL*Plus第一个空字符串代表跳过某个前置参数5g1x2m3p4q是目标 SQL ID最后的Y表示要求生成 profile 建议脚本。如果脚本提示缺少参数多半是版本不同导致参数位置偏移这时最好的做法是先不带参数跑一次交互模式从提示顺序反推位置。我习惯在正式批量前先用一条 SQL 试跑静默模式确认输出文件正常再放开到全量任务。4.3 没有 SQL ID 时的文本模式小心符号与格式差异事故现场经常拿不到 SQL ID只有日志里的一段 SQL 文本。SQLT 也支持按文本抓取但要求文本与共享池里缓存的 SQL 完全一致。这个“一致”包含大小写、换行、空格以及是否带有、#这类特殊字符。由于文本模式对格式敏感跑之前先改 SQL*Plus 的默认行为SQL set define off SQL sqltxtract.sql SELECT * FROM t_order WHERE order_id :v1 AND status 1先执行set define off是为了避免 SQL 文本里的被当成替换变量这是文本模式最容易翻车的地方。另外要注意如果日志里的 SQL 是被应用拼接后的字面量版本而共享池里缓存的是带绑定变量的版本那么按文本抓取会查不到任何结果。真遇到抓不到的情况直接放弃文本模式去 AWR 里按时间范围和资源消耗找 SQL ID 更可靠。4.4 控制收集范围的三个关键开关先取证再决定要不要干预SQLT 的可调参数很多但真正需要关心的就几个。我按“只读取证、不改变数据库行为”的原则推荐首次运行时用保守配置开关作用建议值collect_plan是否采集执行计划Y先看执行计划再判断collect_stats是否采集表和索引统计信息Y直方图和过期统计是常见元凶apply_profile是否直接应用 SQL ProfileN第一次只出报告不改执行计划apply_profile这一个开关最容易让人产生误解。很多人以为抓到慢 SQL 后把这个参数开到 Y 就能自动优化实际上它会生成并加载一个 profile改变后续执行计划。如果没有人工复核就自动应用很可能把原本只影响一条 SQL 的行为扩大到一类 SQL。数据库环境里有过这样的教训某团队开着这个参数跑批处理结果因为 profile 让一批 SQL 全走了新计划性能不升反降。所以生产环境我强烈建议先N跑一轮拿到报告和分析结论后再由人决定是否单独执行 profile 载入脚本。绑定变量和时间范围也是影响抓取结果的两个隐藏参数。如果 SQL 是用字面量执行的同一个 SQL 文本可能对应多个 SQL ID抓取时最好指定你要分析的那一个如果 SQL 已经不在共享池需要通过 AWR 模式读取历史快照那就必须把时间范围缩小到快照覆盖区间内否则查不到数据。5. 从安装到出报告的踩坑记录5 个现场问题与排查思路5.1 在 CDB 根上装完 SQLT切到 PDB 里却找不到对象现象在 19c 容器库上以sqlplus / as sysdba登录默认落在 CDB 根。执行install/sqltinstall.sql一路顺利等切到具体 PDB 里再调用sqltxtract.sql却报对象不存在dba_objects查不到SQLTXPLAIN下的任何对象。原因12c 及以上版本的容器库里CDB 根和 PDB 是隔离的数据字典。SQLT 安装在哪个容器里对象就只存在于哪个容器。刚才的安装实际只发生在 CDB 根而业务 SQL 通常跑在 PDB 里两边根本不在同一个命名空间。解决安装前先确认当前会话的容器。正确做法是用 PDB 的服务名连接或者在安装脚本执行前先alter session set container 你的PDB名把安装动作落进业务所在 PDB。如果已经在 CDB 根装完可以到目标 PDB 里重新跑一次安装脚本不会冲突。注意PDB 的SQLTXPLAIN用户权限只对 PDB 内部生效而抓取 AWR 数据可能还需要额外授权这要视具体包版本而定。5.2 解压后相对路径失效脚本报找不到文件现象把 zip 解压到/opt/sqlt然后在/home/oracle目录下用绝对路径执行/opt/sqlt/sqltxtract.sql屏幕报SP2-0310: 无法打开文件或者脚本运行到一半报找不到utl目录下的库文件。原因SQLT 的脚本内部大量使用相对路径依赖“当前工作目录就是解压根目录”这一假设。从别处调用时SQL*Plus 解析了入口文件的绝对路径但脚本里后续引用utl/xxx.sql时还是从当前目录找目录对不上就断在那里。解决先cd /opt/sqlt再调用脚本保持当前目录是解压根目录。如果脚本里有环境变量位可以显式设置指向解压根目录但我个人更推荐用cd因为这还能保证输出文件落在可预期的地方。这个坑在第一次接触 SQLT 时几乎必踩踩过一次后就再也不会犯。5.3 按文本抓取返回空结果明明日志里就是这么写的现象把应用日志里的 SQL 文本复制出来交给 SQLT 文本模式抓取它提示找不到匹配的 SQL一条都查不到。原因共享池里缓存的 SQL 是格式化后的内部表示应用日志里的是原始拼接文本。两者在换行、缩进、大小写、绑定变量占位符上都可能不一致。还有一个隐蔽因素是等特殊字符没有被set define off禁用被 SQL*Plus 当成了替换变量悄悄处理掉。解决先set define off然后用v$sql或dba_hist_sqltext查原始 SQL 文本再喂给 SQLT。文本模式的定位是辅助工具主路径永远优先用 SQL ID。如果只有日志文本先尝试把文本拆成单行、去掉多余空白再匹配仍不行就改用 AWR 的 top SQL 圈定范围从时间维度找。5.4 报告拿到了但主报告里缺少执行计划段现象抓取成功文件齐全唯独主报告的执行计划部分是空的只有 SQL 文本和一堆统计信息。原因SQL 在执行抓取动作时已经不在 shared pool而 AWR 快照里又没保留它的执行计划。SQLT 是按快照时间和当时的缓存状态取 plan如果那段时间 SQL 没有完整执行或者快照间隔没有覆盖plan 就是缺失的。还有一种常见原因是 collect_plan 开关被前一次交互式回答误设成了 N。解决先把 collect_plan 确认成 Y。如果 SQL 确实已经不在缓存回到应用里让它重新执行一次趁它还留在 shared pool 时立刻用 SQL ID 抓取或者把 AWR 快照时间范围扩大确保 SQL 执行点落在两个快照之间。不要在一份缺失 plan 的报告上强行分析那样和盲猜没有区别。5.5 生成并载入了 SQL Profile执行计划却没有按预期变化现象SQLT 给出了 profile 载入脚本手动执行也没有报错dba_sql_profiles里能看到记录但再次执行 SQL 时执行计划和原来一模一样。原因最典型的是载入的 profile 对应的 SQL ID 和你正在测的 SQL ID 对不上。SQL 文本里只要有一个空格不同SQL ID 就会变化。另一个常见原因是 profile 启用后SQL 还存在于共享池Oracle 没重新硬解析所以在原游标里看不到变化。解决先核对dba_sql_profiles里保存的 SQL 文本和当前 SQL 是否完全一致。确认无误后把这条 SQL 从 shared pool 清掉或者使用alter system flush shared_pool在维护窗口强制重解析再看新执行计划。如果换了计划但性能反而更差还可以用drop_sql_profile回滚这也是为什么我一直强调不要把 apply_profile 默认开 Y 的原因——载入简单退出成本却不低。6. 把 SQLT 从手动抓取变成夜间定时归档AWR Top SQL 自动巡检的一个进阶习惯SQLT 最尴尬的使用场景是问题 SQL 只在深夜批处理出现白天人不在等第二天到现场查SQL 早被刷出共享池只能靠 AWR 历史快照补救。我的习惯是写一个定时任务在批处理结束后自动把 TOP SQL 抓取归档第二天上班直接看报告不用再回溯。#!/usr/bin/env bash . /home/oracle/scripts/env.sh DATE_TAG$(date %Y%m%d) OUT_DIR/backup/sqlt_report/${DATE_TAG} mkdir -p ${OUT_DIR} cd ${OUT_DIR} sqlplus -S / as sysdba EOF set pagesize 0 feedback off linesize 200 trimspool on spool /tmp/top_sqlids.txt select sql_id from ( select s.sql_id, sum(s.elapsed_time_delta) et from dba_hist_sqlstat s, dba_hist_snapshot sn where s.snap_id sn.snap_id and sn.begin_interval_time sysdate - 1 group by s.sql_id order by et desc ) where rownum 10; spool off; EOF while read -r sqlid; do if [ -z ${sqlid} ]; then continue fi sqlplus -S / as sysdba EOF /opt/sqlt/sqltxtract.sql ${sqlid} N Y EOF done /tmp/top_sqlids.txt这段脚本的思路是先通过dba_hist_sqlstat和dba_hist_snapshot关联按前一天的累计执行时间排序取出前 10 个 SQL ID再逐个交给 SQLT 静默抓取。脚本里所有输出都在OUT_DIR目录下生成这样每天的报表自动按日期归档不会被后续任务覆盖。sqlplus -S表示静默模式set pagesize 0去掉分页trimspool on防止行尾残留空格影响后面的循环读取。如果某天跑出来的文件特别大就要检查是不是有异常 SQL 产生了巨大的绑定变量数据而不是脚本有问题。定时任务可以用 cron 固定在批处理结束后一小时执行并配合一个简单的清理策略只保留最近 30 天归档避免磁盘被报告堆满。归档数量多时主报告的 HTML 文件名里已经带 SQL ID按文件名索引就行不需要额外建清单。我曾经为了一次故障分析把几十天的 SQLT 报告散落在不同目录结果整理花了比分析还多的时间之后才养成这种“跑完即归档”的习惯。归档跑完第二天看报告时优先盯两个信号一是执行计划是否与前一天不同二是 rows 估算与实际行数的偏差是否突然放大。只要有变化基本就是当天批处理问题的直接证据。这套流程跑顺之后你不需要等业务方报障才开始查很多隐患会被提前发现希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑