简介:这是一份针对 Oracle 10g 至 19c 各版本的 SQLT(SQL Tuning Advisor Test)性能调优工具包,主要面向数据库管理员、性能优化工程师及中高级开发者,用于诊断低效 SQL、分析执行计划并生成或迁移 SQL Profile。压缩包共 205 个文件,大小约 927KB,包含 160 个 SQL 脚本、38 个 PL/SQL 包源码(pkb/pks)、5 个说明文档及 2 个 HTML 报告;SQL 脚本负责执行调优分析,包体提供底层逻辑,HTML 报告便于直观查看变更与使用说明。目前已有 413 人学习下载。借助这套脚本,用户可以快速评估各版本库中的 SQL 语句,生成更优执行策略,并通过 coe_xfr_sql_profile.sql 将优化建议跨环境迁移,为日常运维和故障排查提供一套现成的工具链。 上个月给一套19c RAC处理性能问题,客户反馈某条核心交易SQL从毫秒级突然恶化到秒级。我当时第一反应不是先翻AWR,而是直接找出存放了很久的一个压缩包:sqlt_10g_11g_12c_18c_19c_5th_June_2020.zip。解压,sqlplus连上数据库,一个sql_id丢进去,等脚本跑完,执行计划、统计信息、绑定变量、优化器参数全部摆到眼前,问题定位非常直接。
这个ZIP是Oracle官方支持体系里流传极广的SQLT工具包,也就是DBA圈子常说的SQLTXPLAIN。文件名里的10g_11g_12c_18c_19c说明它覆盖了当前主流的所有Oracle大版本,后面的日期是2020年6月5日的构建版本。别以为是旧工具,恰恰相反,SQL优化方法论比数据库版本稳定得多,这个工具包到今天依然是排查单条SQL问题最顺手的利器。适合所有Oracle DBA、应用性能优化工程师,以及需要自己处理慢SQL的开发和运维人员参考。
1. 项目概述与工具定位
1.1 这组文件到底是什么
SQLT全称是SQL Tuning,核心作用是对单条SQL做“全信息收集”。它通过一组PL/SQL脚本,把目标SQL的执行计划、优化器统计信息、绑定变量、表结构、索引信息、初始化参数、SQL Profile、SPM以及相关的AWR数据全部抓取出来,最后生成一套HTML+文本报告。Oracle Support工程师在远程分析性能问题时,经常要求客户运行这个工具,目的就是省去反复提问和多次采集的环节。
很多人喜欢先看AWR报告,但AWR是实例级别的性能快照,里面可能有几十条SQL,单条SQL的完整画像其实不够细致。SQLT的思路完全不同,它是单SQL维度,类似给一条语句做“全身体检”。拿到报告后,优化器为什么选错执行计划、统计信息是不是过期、绑定变量有没有问题,基本一眼就能看出来。
解压这个ZIP后,里面通常包含install、reports、scripts、patch之类的目录,以及大量.sql和.plb文件。实际部署时不用关心绝大多数文件,只需要找到安装脚本和后续几个入口脚本。整个工具对数据库是只读操作,不会改任何对象结构,在遇到生产环境疑难SQL时可以放心使用。
1.2 为什么值得收藏这个跨版本工具包
很多工具都有版本适配问题,比如某些诊断脚本在11g正常,到19c却报错。sqlt_10g_11g_12c_18c_19c这个命名就说明它已经考虑了跨版本兼容,实际使用下来,在10g到19c的标准版本上基本都能稳定运行。尤其是现在还有大量系统跑在11.2.0.4和19c上,一个工具包同时覆盖老版本和新版本,确实能省去很多来回找脚本的时间。
另外,SQLT会读取数据库内部视图和字典信息,不同版本内部对象名稍有差异,但这个工具包内部做了大量条件判断,所以能在不同版本上输出格式统一的报告。对经常处理多套环境的人来说,一份报告模板通吃所有数据库,学习成本非常低。这也是我为什么一直把2020年这个版本留在工作机上,虽然之后Oracle也更新过SQLT,但这个版本的核心能力已经足够覆盖绝大多数场景。
值得提醒的是,别把SQLT当成一个自动优化工具。它不负责给出“最优执行计划”,而是负责把诊断证据摆整齐,最终判断还得靠人来下。理解这一点,就不会对工具抱有不切实际的期望。
2. 核心原理与关键细节
2.1 SQLT的工作逻辑:给SQL做“全身体检”
SQLT的执行流程可以分几步:连接数据库后,通过传入的sql_id或SQL文本,先从共享池或AWR中定位目标SQL;然后调用内部包收集SQL执行计划和优化器统计信息,包括表、索引、列上的统计信息;接着读取会话和系统级初始化参数,尤其是影响CBO的关键参数;再抓取绑定变量、SQL Profile、Outline、SPM等信息;最后把收集结果整理成可读的HTML报告。
这个过程相当于自动完成了一次手工诊断操作。如果没有SQLT,DBA通常要执行十几条查询,分别查v$sql、dba_tables、dba_indexes、dba_hist_sqlplan、v$sql_bind_capture等视图,再手工拼一份文档。SQLT把这些工作全部脚本化了,输出格式统一,还能附带优化器估算成本和实际执行统计的对比。
工具里还有一个比较实用的功能:如果SQL已经不在共享池中,SQLT可以通过AWR快照中的历史信息还原当时的执行计划。这对我来说特别有用,很多慢SQL是凌晨发生的,等接手时SQL早就被刷出共享池了,有了AWR里的快照,依然能还原现场。
2.2 三个常用入口脚本
SQLT里有很多脚本,但实际高频使用的入口主要是三个,分别对应不同的排查场景:
| 脚本名 | 输入参数 | 适用场景 |
|---|---|---|
sqltrpt.sql | SQL_ID | 当前或历史SQL快速生成诊断报告,最常用 |
sqltxtract.sql | SQL_ID + SNAP_ID范围 | 从AWR中抽取SQL信息,适合SQL已被刷出共享池的情况 |
sqltcreate.sql | SQL文本 | 构造一个可复现的最小测试案例,适合分析优化器选错计划 |
sqltrpt.sql是最推荐的上手入口。只要有一个sql_id,执行这个脚本后按提示输入参数,脚本会自动把该SQL相关的所有信息收集并生成报告。sqltxtract.sql则适合处理故障SQL已经不在内存中的情况,需要从AWR里找到对应的快照范围,提取难度稍高,但能拿到历史执行计划。
sqltcreate.sql是一个容易被忽略但很实用的脚本。遇到优化器在测试环境无法还原生产行为时,它可以从目标SQL中提取涉及的表结构、数据分布和统计信息,生成一套可以独立运行的小样本库,方便开发人员做本地复现。这也解释了为什么SQLT不仅是DBA的工具,开发团队同样能用上。
3. 实操过程:从解压到生成第一份报告
3.1 解压与部署
拿到sqlt_10g_11g_12c_18c_19c_5th_June_2020.zip后,我习惯先把它传到数据库服务器的/u01/app/sqlt目录下,避免直接在Windows上解压后再上传,因为脚本的换行符和字符集容易出问题。在Linux环境下执行:
cd /u01/app unzip -q sqlt_10g_11g_12c_18c_19c_5th_June_2020.zip -d sqlt cd sqlt ls -la解压后可以看到顶层目录里通常有install、sqlt、utl等子目录,还有一些README或说明文件。第一次使用前务必看一眼README,因为不同构建版本的入口脚本名称可能有细微差异。比如有的版本是sqltrpt.sql,有的版本可能是sqltxtract.sql更全。以解压后的实际文件为准。
此时还要确认数据库环境变量可用。执行sqlplus / as sysdba能正常登录,然后执行show parameter db_version确认版本。SQLT本身不区分容器数据库还是非容器数据库,但后面在19c CDB环境下登录PDB需要注意连接串,这个放到问题排查部分细说。
3.2 安装脚本与权限准备
SQLT虽然多数脚本可以直接以SYS身份运行,但官方推荐在数据库中单独准备一个用户或直接使用SYSTEM。以我的习惯,最省事的方式是以SYS运行安装脚本:
sqlplus / as sysdba SQL> @/u01/app/sqlt/install/sqlt$install.sql执行过程中会创建工具所需的对象和辅助表。如果在19c的PDB中运行,需要先切换到目标PDB:
SQL> ALTER SESSION SET CONTAINER = ORCLPDB; SQL> @/u01/app/sqlt/install/sqlt$install.sql安装脚本一般不会覆盖业务数据,但建议仍然选择在维护窗口或测试库先跑一次。如果安装过程中出现权限不足或对象已存在的报错,多数是因为重复安装导致,可以先查询对象归属再决定是否清理重装。
最小可用权限其实很简单:普通诊断场景,用SYSTEM用户或者具备DBA角色的用户执行入口脚本即可。如果是给开发环境用,单独建一个账号,执行SQLT自带的授权脚本,也可以跑出完整报告。
3.3 根据SQL_ID生成诊断报告
先找出目标SQL的sql_id。常用的查询方式:
SELECT sql_id, sql_text FROM v$sql WHERE sql_text LIKE '%业务关键SQL特征片段%' AND sql_text NOT LIKE '%v$sql%';拿到sql_id后,在sqlplus中执行:
SQL> @/u01/app/sqlt/sqltrpt.sql根据提示依次输入sql_id,报告类型等等。不同版本提示略有不同,通常是输入1选择标准报告,或者直接回车采用默认值。等待执行时,SQLT会在当前目录下生成以sqlt_开头的输出文件夹,里面包含HTML报告和附属的文本文件。整个收集过程可能持续几分钟,取决于SQL的复杂度和系统负载。
我最常用的方式是直接指定输出目录,避免在当前目录下堆积大量文件:
mkdir -p /tmp/sqlt_out cd /tmp/sqlt_out sqlplus / as sysdba SQL> @/u01/app/sqlt/sqltrpt.sql报告生成后,目录下会出现类似sqlt_sql_text.html、sqlt_sql_plan.html、sqlt_sql_stats.html的文件。一般从sqlt_sql_plan.html看执行计划变化,从sqlt_sql_stats.html看统计信息是否过期,从sqlt_main.html看整体摘要。
3.4 报告里该重点看什么
拿到报告,我通常按这个顺序看:先看报告顶部的“建议”(Recommendation)部分,SQLT会基于数据字典信息给出一些优化提示,比如“某个表缺少统计信息”或“索引未被使用”。然后看执行计划栏,重点关注有没有大的全表扫描、索引跳跃扫描、笛卡尔连接等异常操作。最后看绑定变量,很多性能抖动源于绑定变量值的倾斜。
在19c环境中,还要额外关注自适应计划(Adaptive Plan)和统计信息反馈机制。SQLT报告里会标记出执行计划是否含有ONLINE或ADAPTIVE属性,遇到这类情况,说明优化器在运行时才最终确定计划,这往往是测试环境正常、生产环境偶发慢SQL的主要原因。
报告里的Optimizer Cost和Actual Rows对比也很有价值。如果Cost显示很低,但实际行数很大,说明优化器对基数估算出现了严重偏差。此时顺着报告里的表统计信息、列直方图往下排查,基本都能找到根因。
4. 常见问题与排查技巧实录
4.1 ORA-00942 / ORA-01031权限问题
很多第一次跑SQLT的人会碰到类似的报错:
ORA-00942: table or view does not exist ORA-01031: insufficient privileges原因通常是当前用户没有权限读取v_$开头的动态性能视图。解决办法很简单,以SYS身份授权给当前诊断用户,比如:
GRANT SELECT ON sys.v_$sql TO sqlt_user; GRANT SELECT ON sys.v_$sql_plan TO sqlt_user; GRANT SELECT ON sys.v_$sql_bind_capture TO sqlt_user;生产环境中可能涉及的视图比较多,手工一个个授权太繁琐。SQLT安装包里一般自带grant_sqlt_privs.sql之类的授权脚本,直接执行一次更稳妥。如果使用的是SYSTEM账号,通常自带DBA角色,不需要额外授权。
4.2 19c CDB/PDB环境下的坑
19c默认是容器数据库,很多DBA习惯直接连CDB$ROOT执行脚本。但SQLT如果以公共用户运行,部分动态视图在CDB根容器下访问范围有限,容易遇到“表或视图不存在”或者收集不到PDB内SQL的问题。解决方案是明确连到目标PDB:
sqlplus system@orclpdb或者在sqlplus中切换容器:
SQL> ALTER SESSION SET CONTAINER = ORCLPDB; SQL> @/u01/app/sqlt/sqltrpt.sqlPDB下运行通常会更顺畅,也更能反映业务SQL的真实状态。还有个细节是,19c中如果sql_id属于某个PDB,在CDB根下直接按sql_id查询很可能查不到,最好指定con_id或在PDB内查询。
4.3 收集时间长、占用临时空间大
SQLT收集的信息量很大,默认全量模式可能在一张大表环境下跑很久,甚至把临时表空间撑满。我实际操作中遇到过临时表空间直接爆掉的情况。解决方法有几个方向:
- 安装SQLT时单独创建一个专用的临时表空间,并指定给SQLT用户使用;
- 在执行入口脚本时选择精简模式,比如
sqltrpt.sql一般会提供BASIC、STANDARD、EXTENDED等选项,日常排查选BASIC或STANDARD就够; - 给输出目录设置独立磁盘,避免写到根分区导致磁盘满。
还有一些版本支持设置环境变量SQLT_OUTPUT_DIR来指定报告输出位置。把输出目录指到空间充足的分区,能避免生成中间文件时报No space left on device。
4.4 从AWR提取SQL时报Snapshot Too Old
当目标SQL已经不共享池时,使用sqltxtract.sql从AWR提取历史信息是一个好办法,但偶尔会遇到“snapshot too old”之类的问题,通常是AWR快照保留时间太短,或者查询范围跨过了快照清理节点。解决思路是先确认SQL对应的快照范围:
SELECT snap_id, begin_interval_time, end_interval_time FROM dba_hist_snapshot ORDER BY snap_id DESC;确认目标SQL在快照中存在后,再重新用sqltxtract.sql精确指定SNAP_ID范围。如果AWR中没有记录,那就要靠应用端日志或者监听日志中的SQL文本来恢复现场。此时SQLT也可以直接用SQL文本方式生成报告,但缺少了当时的执行环境,信息量会打折扣。
还有一个小技巧,如果现场还能登录数据库,在SQL被刷出共享池之前,可以先手动用dbms_shared_pool.keep把SQL固定住,再运行SQLT收集。这样能避免后续排查时SQL突然丢失,属于预防性操作。
5. 实际使用中的几点心得
这个工具包我用过很多次,最大的体会是它帮我把“凭经验猜”变成了“拿报告说话”。以前遇到SQL性能问题,可能要反复翻好几张视图,现在一张报告就能覆盖绝大部分诊断场景,尤其是在跨版本环境里,输出格式统一,减少了很多沟通成本。
我自己的习惯是,每次拿到SQLT报告后,先复制一份到本地留存,同时用文本工具把绑定变量具体值打码,避免敏感信息泄露。毕竟报告里包含了实际SQL文本,里面可能涉及业务字段和参数,外发时要注意脱敏。
另外,SQLT生成的报告可以和Oracle SPM结合起来用。当报告定位到执行计划不稳定导致性能下降时,直接在19c里用DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE把好的计划固定为基线,比手工写hint更稳固。SQLT把相关SQL的Plan Hash Value都列出来了,操作起来非常方便。
如果你还没用过SQLT,建议找一个测试库先跑一遍完整流程,理解一下报告结构。等真正遇到生产性能问题时,你会感谢自己提前做了这个准备。
本文还有配套的精品资源,点击获取