顾问是一套「分析、建议、依据」的框架:你给它一个任务,它跑完给你建议, 每条建议后面附一份「为什么这么建议」。看不懂依据,就等于没用它。 两个最常用的:SQL 调优顾问管「这条 SQL 怎么写」,SQL 访问顾问管「该建什么索引」。 它们都会错,因为只看成本模型,不懂你的业务。
15.5 是我们自己写脚本比耗时。这一篇是让数据库自己分析,直接给出建议清单。 但两者的关系不是替代,是接力:顾问帮你把范围从「几万条 SQL」缩到「五个候选索引」, 验证还得靠 15.5 那套办法。
一、顾问是什么:一套统一的框架
Oracle 里带「Advisor」的东西有十几个,但结构是一样的:
建任务(task)、执行、出建议(recommendation)、附依据(finding)。
所以学会一个,其余的看文档就会用。
它们的数据都落在同一组视图里,四个后缀分别对应四件事:
_TASKS 是任务、_RECOMMENDATIONS 是建议、_FINDINGS 是依据、
_ACTIONS 是「具体该执行什么命令」。
前缀都是 DBA_ADVISOR。
DBA_ADVISOR_ACTIONS 这一列最实用:它直接把「该敲的 SQL」写成文本给你,复制就能跑。
但正因为太方便,很多人在这一步犯错——看都不看就整段执行。
常见的几个顾问,各管一段:
| 顾问 | 管什么 | 什么时候用 |
|---|---|---|
| ADDM | 整体诊断:这一段最耗什么 | 每天看 AWR 时顺手看 |
| SQL 调优顾问 | 单条 SQL 怎么变快 | 找到 Top SQL 之后 |
| SQL 访问顾问 | 该建哪些索引和物化视图 | 一批 SQL 都慢的时候 |
| Segment Advisor | 哪些段有空间可回收 | 表空间吃紧时 |
| Undo Advisor | 回滚段该留多大 | 长事务报快照过旧时 |
二、SQL 调优顾问
它的输入可以是单条 SQL(给 SQL_ID 或 SQL 文本),也可以是一个 SQL 集。
三个包函数走完:DBMS_SQLTUNE.CREATE_TUNING_TASK 建任务、EXECUTE_TUNING_TASK 执行、
REPORT_TUNING_TASK 出报告。
它给的建议分四类,风险从低到高:
| 建议类型 | 它想做什么 | 风险 |
|---|---|---|
| 统计信息 | 这张表的统计信息过时了,重收一遍 | 最低 |
| 索引 | 建一个索引,能让这条 SQL 少扫很多块 | 中,写会变慢 |
| SQL 改写 | 这条 SQL 有个等价但更快的写法 | 中,要回归测试 |
| SQL Profile | 给优化器补一组修正系数 | 高,升级后可能失效 |
四类里最值得先看的是第一类:统计信息。 因为它什么都不改,只是让优化器知道真实情况——这一条能解决的比例,比很多人想的高得多。 最玄的是 SQL Profile:它不改 SQL、不建索引、不改数据, 只往优化器里塞一份「这个条件下实际有多少行」的修正。 效果是优化器突然「算对了」,于是选对了路。 代价是这份修正绑在 SQL 的文本特征上,SQL 一改、版本一升,可能就废了。 所以 Profile 的定位是「救火」,不是「治本」。 治本还是收统计信息,或者干脆把 SQL 改成优化器不用猜的写法。
三、SQL 访问顾问
它管的不是一条 SQL,而是一批 SQL:给定工作负载,它算出「该建哪些索引、该建哪些物化视图、
该按什么分区」。
这是它跟调优顾问最大的区别——调优顾问一次只看一条,访问顾问一次看一整批。
用法比调优顾问啰嗦,要五步:
① DBMS_ADVISOR.CREATE_TASK('SQL Access Advisor')——建任务。
② CREATE_SQLWKLD_REFERENCE——把负载挂在任务上。
③ ADD_STS_REF——关联一个 SQL 集。
④ SET_TASK_PARAMETER——设「只考虑索引」还是「也考虑物化视图」。
⑤ EXECUTE_TASK 再 GET_TASK_REPORT——执行并取报告。
第 ④ 步那个参数最关键,因为它决定了搜索空间:
只考虑索引,它就在「哪些列、什么顺序」里搜;
允许物化视图,它还会考虑「要不要预先算好一张汇总表」。
后者收益大得多,代价也大得多——物化视图要占空间、要刷新,刷新本身又是一笔开销。
它还有个参数叫「空间限制」。
设了之后,它会在「这么多空间里怎么建最划算」的约束下给方案。
这个参数很实用:不加限制,它会建议你建八个索引;
加了限制,它会告诉你「前三个收益占八成,后五个只占两成」。
四、建议怎么读:按风险排序
一条建议值不值得做,看三件事:改了什么、收益多大、代价是什么。 顾问只保证第二件,前两件得你自己判断。 收益这一栏它给的是「估算的改善百分比」,来源是成本模型。 成本模型准不准,取决于统计信息准不准——所以统计信息过时的时候,它连收益都算错。 代价这一栏它基本不给。 一条「建索引」的建议,报告里不会写「你的插入会慢多少」。 这不是它藏私,是它压根不算这个——它只看查询侧的成本。 所以读报告的顺序是: 先看「统计信息」类建议(零风险,直接做)→ 再看「SQL 改写」(要测试, 收益通常大)→ 再看「索引」(要算写代价)→ 最后看「Profile」和「固定基线」(救火用)。 顺序反了会怎样:先做索引那几条,写入变慢,然后把问题归到「顾问不靠谱」上。 其实顾问没说错,是你漏算了写代价。
五、顾问用错了会怎样
第一种错:喂假负载。
「我随便挑了几条慢 SQL 喂进去」——顾问给你的就是「只优化这几条」的方案,
别的 SQL 可能因此变慢。
它不懂你的业务,只知道你喂了什么。
第二种错:照单全收。
一次建八个索引,读可能快了三成,写慢了一倍。
这笔账顾问不替你算。
第三种错:不看依据。
报告里 DBA_ADVISOR_FINDINGS 那几行才是「为什么」,跳过它就等于没看报告。
比如它说「建索引能省 60% 的块访问」,依据是「这条 SQL 当前扫了 20000 块」——
这个数字你可以自己去 V$SQL 核一遍,核不上就说明它估错了。
所以顾问的正确角色是「帮你缩小范围」,不是「替你做决定」。
它把几万条 SQL 缩成五个候选索引,剩下的判断——该建哪个、写代价能不能承受、
业务上能不能接受——还得人来做。
最后拍板的人,得看得懂计划,也看得懂业务。
六、动手 Demo:简化版访问顾问:从负载算出该建什么索引,再真建出来验证
这一篇的 Demo 做三件事:算「哪些列被最多 SQL 引用」、照建议建索引、 再把整批负载重跑一遍看总收益。 它比真顾问简陋得多,但三个环节一个不少:输入负载、输出建议、验证收益。 而这三个环节,正是「顾问能不能信」的全部依据。
# 简化版「访问顾问」:按被引用的列排序,再照建议建索引验证
import collections
cnt = collections.Counter(tag for sql, tag in load) # load 同上
for col, n in cnt.most_common():
print(" %-12s %d" % (col, n))
# 顾问的输出大致长这样:
# 建议建索引 big(deptno) 被 40 条 SQL 引用
# 建议建索引 big(amt) 被 20 条 SQL 引用
# 建议建索引 big(deptno, flag) 被 10 条 SQL 引用
# 照第一条建出来,整批负载重跑一遍
t0 = sum(best(lambda s=s[0]: d.execute(s).fetchone()) for s in load)
d.execute("create index i_a_deptno on big(deptno)")
d.execute("create index i_a_amt on big(amt)")
t1 = sum(best(lambda s=s[0]: d.execute(s).fetchone()) for s in load)
print("建索引前 %.4f 秒,建索引后 %.4f 秒,快了 %.2f 倍"
% (t0, t1, t0 / t1))
实跑输出:
--------------------------------------------------------------------
第六十九课:性能顾问——它给建议,你负责验证
--------------------------------------------------------------------
**Oracle 有两个常用的顾问:SQL 调优顾问管「这条 SQL 怎么写」,SQL 访问顾问管「该建什么索引」。**
**这一课跑一个简化版的访问顾问:从负载里算出该建哪些索引,再真建出来验证。**
一、顾问的输入:工作负载
--------------------------------------------------------------------
**真实用法是把一个 SQL 集(SQL Tuning Set)喂给它。**
**这里直接用一个列表:80 条 SQL,各自带了「访问了哪些列」。**
列组合 被引用次数
----------------------
deptno 40
amt 20
name 10
flag+deptno 10
已选择 4 行
二、顾问的输出:建议 + 理由 + 预估收益
--------------------------------------------------------------------
**真实的 `DBMS_ADVISOR` 输出是一张表:建议什么、为什么、预估能省多少成本。**
**这里的「预估」是用「这条 SQL 现在的耗时 × 出现次数」算的:**
建议 影响条数 当前总耗时
-----------------------------------------
给 deptno 建索引 40 1.0937 秒
给 amt 建索引 20 0.5391 秒
给 name 建索引 10 0.2983 秒
给 flag+deptno 建索引 10 0.3497 秒
已选择 4 行
**「影响条数」和「当前总耗时」两列一起看才有意义。**
**一条 SQL 慢但只跑一次,和一条 SQL 快但跑一万次,该优化的是后者。**
**顾问的排序依据是「总成本」,不是「单次成本」。**
三、照建议建出来,验证
--------------------------------------------------------------------
整批负载总耗时
-------------------------
建索引之前 2.2885 秒
建索引之后 0.5189 秒
已选择 2 行
**快了 4.41 倍。**
**但这不是「照单全收」的理由——**
**索引有写代价:每次 `INSERT`、`UPDATE`、`DELETE` 都要维护它。**
**代价实测:带两个索引插 5000 行用了 0.0670 秒。**
**可以把这两个索引删掉再跑一次对比——你会看到写变快了、读变慢了。**
四、顾问的三条使用规矩
--------------------------------------------------------------------
1. 一定用真实负载 别拿自己编的几条 SQL 当样本
2. 建议要逐条验证 顾问也会错,它只看成本模型
3. 建完再看写性能 读快了、写慢了,要算总账
**第一条最容易违反。**
**「我随便挑了几条慢 SQL 喂进去」——顾问给你的就是「优化这几条」的方案,别的 SQL 可能因此变慢。**
**它不懂你的业务,只知道你喂了什么。**
**所以顾问的角色是「帮你缩小范围」,不是「替你做决定」。**
**最后拍板的人还是得看懂计划、看得懂业务。**
第一段是顾问的输入:80 条 SQL,各自带了「访问了哪些列」。
真顾问喂的是 SQL 集,这里用列表代替,省掉抓负载那一步——
但「按列统计引用次数」这个逻辑是一样的。
第二段是顾问的输出:建议、理由、预估收益。
注意 deptno 被 40 条引用,是 amt 的两倍——这就是它排在第一位的依据:收益面最广。
第三段照建议建两个索引,整批负载从 2.2885 秒掉到 0.5189 秒,快 4.41 倍。
这个数字比 15.5 那个只加一个索引的 2.17 倍更好看,但代价也更大:多了两个索引要维护。
第四段是这一篇最要紧的一句:这不是「照单全收」的理由。
实测带两个索引插 5000 行用 0.0670 秒——写是有代价的,只是顾问不算这笔账。
报告里永远不会出现「你的插入会慢多少」这一栏。
最后那段三条规矩,其实是在说一件事:顾问的输出是【候选】,不是【结论】。
候选要变成结论,中间必须过一遍验证——也就是 15.5 那套重演。
所以第 15 章这六篇是一个闭环:写 SQL、选索引、看计划、量改前量改后、让顾问缩范围,
最后回到量一遍。
30 秒实验
- 把
load里deptno那 40 条删到只剩 5 条,再跑一次顾问统计。 看建议的顺序会不会变、amt会不会升到第一。 - 只建
amt那个索引,不建deptno的,跑一遍总耗时。 和建两个的 4.41 倍比一下,看「少建一个」亏多少——这就是边际收益。 - 在
big上加一个(deptno, amt)复合索引,把= deptno和= amt两类查询都覆盖住。 看能不能用一个索引干两个索引的活、写代价是不是少了一半。
常见误区
- 「顾问说建就建」。 顾问只算读的收益,不算写的代价。建完要看插入、更新、删除有没有变慢。
- 「顾问给了八个索引,全建上」。 索引之间会互相干扰, 而且每个都要维护。边际收益很快就会掉到零以下。
- 「SQL Profile 是万能药」。 它是把修正系数挂在 SQL 特征上。 SQL 一改、版本一升就可能失效,还可能把错误的计划锁得更死。
- 「顾问说收益 60%,就真能快 60%」。 那是成本模型算出来的估算值。 统计信息不准的时候,连这个数都是错的。
自测三问(点开对答案)
1. 所有顾问共用的四步结构是什么? —— **建任务、执行、出建议、附依据。** 数据分别落在 `DBA_ADVISOR_TASKS`、`_RECOMMENDATIONS`、 `_FINDINGS`、`_ACTIONS` 里。 2. SQL 调优顾问的四类建议里,哪一类风险最低? —— **收集统计信息。** 它什么都不改,只是让优化器知道真实情况,**所以应该永远先做这一条。** 3. SQL Profile 和索引,最根本的区别是什么? —— **Profile 不改数据结构、不改 SQL,只改优化器的估算;** **索引是实打实加了一个对象,读变快、写变慢。** 4. 为什么说顾问的输出是「候选」不是「结论」? —— **因为它只按成本模型算读的收益,不算写的代价,也不懂业务。** **所以每条建议都得自己验证一遍。**第 15 章到这里结束。下一篇进第 16 章:备份与恢复。 调优是「让它跑得快」,备份是「让它丢了能回来」—— 后者在生产上优先级更高,因为快的系统丢了数据也等于零。
- ☐ (本篇完,下一篇 16.1 备份与恢复概述)
相关笔记
- 数据库和 SQL 重演
- 优化器的使用
- 合理使用索引
-
- 🐬 MySQL必知必会/00-索引
- 🏺 Oracle从入门到精通 · 索引