
Oracle执行计划分析从看懂到会用一篇讲透执行计划的原理与实操做数据库工作的人几乎每天都离不开执行计划。慢查询、CPU飙高、锁等待、报表跑不出来——绝大多数性能问题的根源最后都会落到执行计划上。Oracle的执行计划说白了就是优化器告诉我们“我打算怎么去拿这些数据”的一张路线图。看懂它你就能解释为什么同样的SQL有时候跑0.1秒有时候跑10秒看不懂它调优就只能靠瞎猜加索引。这篇文章我想系统地聊一聊Oracle执行计划分析的完整思路从最基础的执行计划生成、查看方法到核心的访问路径和连接方式解读再到实际工作中怎么通过执行计划定位问题、验证优化效果。内容尽量按“先看懂、再会查、最后能用”的顺序来写适合刚接触Oracle优化的开发人员也适合做了几年DBA但想系统梳理一遍执行计划体系的朋友。1. 执行计划到底是什么优化器的路线图1.1 优化器是怎么“想”的执行计划是Oracle优化器CBOCost-Based Optimizer基于统计信息、对象结构、系统参数等多种输入为一条SQL计算出来的最优或者接近最优执行路径。它本质上是一个由一系列操作符Operation组成的树状结构树的每个节点都代表一个具体的执行步骤比如全表扫描、索引范围扫描、哈希连接、排序等。理解执行计划的第一步是理解优化器的工作逻辑。CBO会给每条可能的执行路径估算一个成本Cost这个成本是一个无量纲的相对值综合了I/O代价、CPU代价、内存使用、网络传输等多个因素。优化器最终会选择它认为成本最低的那条路径。但要注意“成本最低”不等于“实际最快”因为成本估算依赖统计信息如果统计信息过期、缺失或者被直方图误导优化器就会做出错误的选择。用生活化的例子来类比执行计划就像导航软件给出的路线。导航会根据当前路况、红绿灯数量、道路等级等计算到达时间选出一条“最优路线”但如果地图数据是三个月前的某条路实际上已经封了导航给出的路线就会不合理。Oracle的统计信息就相当于导航的地图数据地图老化了再好的算法也白搭。1.2 为什么看懂执行计划比记住结果更重要很多人在学习Oracle时喜欢背各种“优化技巧”比如“小表驱动大表”“避免隐式转换”“不要在索引列上加函数”。这些规则确实有用但它们都有适用条件死记硬背很容易出错。真正靠谱的做法是拿到一条慢SQL先看它的执行计划搞清楚它慢在哪一步再根据执行计划里的线索决定怎么改。执行计划能告诉你的信息比你想的多得多哪个步骤消耗的行数最多、哪个步骤返回的行数和预估行数差距最大、哪些地方出现了排序或临时表空间使用、表之间的连接顺序是什么、是否出现了笛卡尔积、是否存在类型转换导致索引失效……这些全是定位性能瓶颈的直接证据。与其盲目加索引不如先花两分钟看懂执行计划到底在干什么。2. 执行计划的5种查看方式从简单到进阶2.1 EXPLAIN PLAN FOR最基础的方式最传统的查看执行计划的方式是使用EXPLAIN PLAN FOR语句然后在PLAN_TABLE里查询结果。具体操作是EXPLAIN PLAN FOR SELECT * FROM t_order WHERE order_date DATE 2024-01-01 AND order_date DATE 2024-02-01; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);这种方式的优点是简单直接不需要任何额外权限而且不会真正执行SQL对线上环境零压力。但有一个重要缺陷它只展示优化器“预估”的执行计划不反映SQL实际运行时的真实情况。如果统计信息不准确或者存在绑定变量窥探等行为EXPLAIN PLAN显示的路径和实际执行路径可能完全不同。2.2 DBMS_XPLAN.DISPLAY_CURSOR看到真实执行情况比EXPLAIN PLAN更进一步的是通过DBMS_XPLAN.DISPLAY_CURSOR查看SQL游标中缓存的执行计划。这个视图展示的是SQL实际执行时使用的计划包含了真实的执行信息比如每个步骤的Actual Rows实际行数、Physical Reads物理读、Logical Reads逻辑读等。-- 先找到SQL的SQL_ID SELECT sql_id, sql_text FROM v$sql WHERE sql_text LIKE %t_order%; -- 查看该SQL的真实执行计划 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id_这里替换, 0, ALLSTATS LAST));这里有个关键点加不加ALLSTATS LAST参数差别很大。ALLSTATS LAST能显示出每个步骤的实际执行行数、逻辑读、物理读以及内存和磁盘排序的情况。只有拿到这些实际数据才能判断优化器预估的“偏差”有多大——预估100行实际跑100万行这就是典型的统计信息失真信号。2.3 AUTOTRACE快速验证SQL执行情况在SQL*Plus或者SQLcl里AUTOTRACE是日常排查的一个利器。设置AUTOTRACE ON后执行SQL时会同时打印执行计划和统计信息SET AUTOTRACE ON; -- 或者只查看执行计划不显示结果集 SET AUTOTRACE ON EXPLAIN;AUTOTRACE的好处是操作简单、反馈即时适合临时验证某条SQL是否走了预期路径。唯一需要注意的是AUTOTRACE在输出统计信息的情况下会真实执行SQL如果这条SQL本身很重比如全表大扫描执行成本需要考虑。所以线上谨慎使用简单查询问题不大。2.4 可视化工具PL/SQL Developer与Toad日常开发中我常用PL/S Developer或Toad看执行计划。在PL/S Developer里打开SQL窗口选中SQL按F5就能生成执行计划树Toad里可以右键选择Explain Plan并且以图形化方式展示执行树和成本占比。这类工具的好处是直观能看到每个步骤的成本占比和缩进层级坏处是它们大多基于EXPLAIN PLAN FOR看不到实际执行的行数信息所以定位复杂问题时我还是习惯回SQL*Plus看真实计划。2.5 SQL Monitor重的SQL和并行查询首选Oracle从11g开始提供了SQL Monitor功能专门针对执行时间超过5秒或者并行执行的SQL自动捕获详细的执行统计信息。通过下面的语句可以查看SELECT dbms_sqlmonitor.report_sql_monitor(sql_id sql_id_这里替换, type TEXT) FROM dual;SQL Monitor的报告比DBMS_XPLAN更丰富包含每个步骤的持续时长、CPU时间、等待事件、缓冲获取次数、并行度等信息还能看到SQL执行的进度百分比。对于ETL作业、大批量更新、并行全表扫描这类重SQLSQL Monitor才是真正的“照妖镜”。3. 执行计划核心概念访问路径、连接方式与排序3.1 访问路径怎么拿数据执行计划中最底层的操作符是“表的访问路径”它决定了Oracle从存储中取出目标数据的方式。常见的有TABLE ACCESS FULL全表扫描把整张表的所有数据块全部读进来再过滤。适合大表全量处理或小表无索引查询但如果是大表上的低频过滤查询往往就是性能瓶颈。TABLE ACCESS BY INDEX ROWID先通过索引找到ROWID再通过ROWID回表取数据。索引只保存键值和ROWID真正要取的列数据在表里所以需要多一次回表操作。INDEX UNIQUE SCAN唯一索引等值查找最多返回一行速度最快。INDEX RANGE SCAN索引范围扫描用于范围条件、模糊匹配前缀、IN列表等场景返回多行。INDEX FULL SCAN索引全扫描虽然叫Full Scan但它扫的是整个索引块通常比全表扫描快尤其是当查询需要的列全部包含在索引中时Covered Index可以完全跳过回表。INDEX SKIP SCAN跳扫适用索引前导列选择性差、后续列选择性好的情况但效率通常不如设计合理的组合索引。HASH SCAN / BITMAP SCAN等用于特定存储和访问场景如位图索引在低基数列上的场景。看执行计划时要特别注意TABLE ACCESS FULL出现在一个选择性很强的过滤条件下——如果过滤条件能把数据从1亿行缩小到10行却又去全表扫描这通常是索引缺失或索引失效的信号。3.2 连接方式多表关联的策略多表关联的SQL是性能问题的高发区连接方式的选择决定了中间结果集的大小和消耗。Oracle主要提供三种连接方式NESTED LOOPS嵌套循环适合一个小表驱动表连接一个大表且被驱动表上有高效索引的场景。它像两层循环外层表每取一行就在内层表里通过索引查找匹配行。总代价约等于外层行数乘以内层单次查找代价。如果外层表返回很多行内层索引又不好嵌套循环会非常慢。HASH JOIN哈希连接适合两个表都比较大或者没有合适的索引连接条件的场景。它把较小的表构建成哈希表放到内存里然后扫描大表对哈希表探测匹配。没有索引也能高效工作但要消耗PGA内存如果内存不够会溢出到临时表空间产生磁盘排序代价。MERGE JOIN排序合并连接适合两个表本身已经有序或者连接条件是等值且两个输入都较大且可排序的场景。它先把两边按连接列排序再用类似归并的方式匹配。这种连接方式现在用得相对少因为在很多场景下HASH JOIN效率更高但如果是非等值连接比如BETWEEN条件归并连接依然有用武之地。看执行计划时连接顺序和连接方式同等重要。执行计划缩进层级越深代表越先执行而PLAN_TABLE或DBMS_XPLAN输出里的缩进顺序通常是从上往下表示执行顺序注意OUTPUT行的顺序并不完全等价于真实执行顺序更准确的方法是看子节点上方的父节点ID关系。3.3 排序与聚合操作SORT ORDER BY、HASH GROUP BY、WINDOW SORT排序是消耗资源的大户。看到SORT ORDER BY、SORT GROUP BY、HASH GROUP BY等操作符时要评估排序的数据量如果排序行数达到几十万甚至上百万PGA内存装不下就会溢写临时表空间产生严重的I/O。大数据量排序最直接的优化方式一是减少排序数据总量尽量在SQL上层把where条件下推二是利用索引有序性消除排序比如对有序索引列进行GROUP BY或ORDER BY。聚合操作中HASH GROUP BY在Oracle 10g以后的执行计划中非常常见它比SORT GROUP BY在内存管理上更灵活。如果执行计划里出现HASH GROUP BY并且伴随大量的“TEMP SPILL”信息说明哈希桶溢出到了磁盘要想办法增大PGA或改写SQL减少分组基数。WINDOW SORT则是分析函数ROW_NUMBER()、RANK()等触发的排序操作分析函数用得越多排序压力越大。3.4 执行计划中的关键字段别再只傻看Cost了很多初学者拿到执行计划只会看Cost数字这其实是个误区。Cost值只是优化器的内部估算同一个SQL在不同参数、不同统计信息下的Cost完全不可比。真正值得关注的是Rows预估行数和A-Rows实际行数两者偏差大统计信息一定有问题。Time预估时间和A-Time实际时间同样偏差意味着优化器模型与实际不符。Buffers / Reads逻辑读和物理读的数值决定了SQL的I/O压力。Starts某个步骤被启动的次数。对于嵌套循环来说内层表如果被启动了上百万次即使单次很快总时间也吓人。所以DBMS_XPLAN.DISPLAY_CURSOR配合ALLSTATS LAST输出的这些字段才是执行计划分析里最有价值的信息而不仅仅是看“有没有走索引”。4. Oracle EBS场景下的执行计划分析非标工单、WIP核心表的SQL调优实战4.1 EBS系统的SQL特点复杂视图、多组织、多层嵌套聊Oracle执行计划绕不开Oracle EBS这个重量级场景。搜热词里出现大量EBS相关内容不是偶然——EBSEnterprise Business Suite本身就是建立在Oracle数据库之上的企业应用系统它的SQL复杂度和性能问题常年“拔尖”。EBS里最典型的就是制造模块WIP的非标工单Non-Standard Work Order查询。这类查询涉及的SQL常常是几个大型视图套着子查询再关联十几个表SQL文本动不动上千行。EBS的APPS Schema下成百上千的视图层层嵌套加上多组织访问控制MOAC、多语言支持、灵活字段Flexfield等因素优化器很难精准估算每个视图的返回行数。这就导致EBS里经常出现执行计划对某些视图预估只有几行、实际跑出几百万行的惨案。4.2 一个WIP非标工单SQL的调优实战记录之前遇到一个EBS WIP相关的报表每天固定时段跑得很慢要二十多分钟影响月结进度。这条SQL的核心是查非标工单的物料、工序和成本信息关联了WIP_OPERATIONS、WIP_DISCRETE_JOBS、WIP_OPERATION_RESOURCES、MTL_SYSTEM_ITEMS_KFV等核心表。第一步是抓真实执行计划用前面讲的DBMS_XPLAN.DISPLAY_CURSOR抓取SQL_ID对应的计划加了ALLSTATS LAST。关键发现如下计划里WIP_OPERATIONS走了全表扫描但它的过滤条件org_id和wip_entity_id其实有索引可用。奇怪的是索引存在但没用上。MTL_SYSTEM_ITEMS_KFV是一个带FND多语言支持的视图解析后变成了MTL_SYSTEM_ITEMS_TL和MTL_SYSTEM_ITEMS_B两张表的UNION ALL优化器对视图内部的基数估算偏差巨大——预估20行实际返回3.8万行。三张表之间用了NESTED LOOP而且驱动表选择有误导致内层表被反复扫描逻辑读超过1亿。排查后的处理方案第一更新了WIP_OPERATIONS、MTL_SYSTEM_ITEMS_B等表的统计信息特别收集了直方图修正基数估算偏差。第二将视图MTL_SYSTEM_ITEMS_KFV改写成直接关联底层表绕开多语言视图的UNION ALL判断减少优化器负担。第三增加/* LEADING */提示强制指定驱动表顺序让WIP_DISCRETE_JOBS先作为驱动表走索引再用NESTED LOOPS连接。第四把原来对WIP_OPERATIONS全表扫描的过滤条件补上函数索引消除隐式转换导致的索引失效。经过这四步调整后这条SQL从20多分钟降到了40秒以内。这里要特别强调的是我没有一开始就动SQL结构而是先看执行计划确认每一个瓶颈点再逐个击破。如果一上来就重写SQL很可能只解决了表面问题而且给后续维护带来巨大麻烦。4.3 多组织环境MOAC下的执行计划陷阱EBS的多组织访问控制是在SQL里自动加一系列ORG_ID过滤条件的机制。这种机制在应用层透明但对数据库优化器是额外的负担。特别是当数据跨越多个Operating Unit时加上的过滤条件通常是“ORG_ID IN (:1, :2, :3...)”这种绑定变量列表优化器对列表长度的基数估算往往不准确。在分析EBS SQL时我常常会先关掉MOAC或者简化ORG_ID条件来测试基本路径等确认了核心的关联方式没问题再把MOAC的复杂度加回去。这不是逃避MOAC而是把变量拆开排查避免多组织过滤和表关联方式混在一起难以定位。4.4 EBS成本计算与PAC成本法对执行计划的影响EBS的PACPeriodic Average Cost成本法在计算期间成本时会执行大量INSERT、UPDATE、DELETE混合的复杂存储过程。这类存储过程的SQL有个共同问题大量使用动态SQL和临时表并且同一段代码在不同期间、不同数据集上执行时数据分布差异极大。我遇到过PAC成本卷积时的一条SQL平时跑10秒但一到数据量大的期间就跑出完全不同的执行计划——优化器选择的HASH JOIN变成了MERGE JOIN临时表空间被吃爆。原因是成本计算时的绑定变量值不同直方图统计没有覆盖新数据分布。处理方式是给这条SQL的绑定变量使用SQL Profile固定执行计划同时在该期间前刷新相关表的统计信息。这类问题背后有个通用原则对于数据分布随业务周期剧烈变化、但SQL文本固定的场景固定执行计划通过SQL Plan Baseline或SQL Profile远比“每次都由优化器自由发挥”更可靠。这是EBS运维中一个非常值得重视的经验。5. 执行计划分析流程化从拿到慢SQL到确认优化效果的完整步骤5.1 第一步抓SQL与执行环境处理一条慢SQL第一步不是改SQL而是把现场信息完整抓下来。需要确认的信息包括SQL文本必须从v$SQL或应用日志中拿完整文本尤其是EBS里可能是上千行的复杂文本。SQL_ID和Plan Hash Value记录执行计划版本方便后续对比。绑定变量查询v$SQL_BIND_CAPTURE确认实际传入的变量值这直接影响执行计划。会话等待事件如果SQL还在运行从v$SESSION_WAIT查看具体在等待什么是CPU还是I/O还是锁。当时系统负载通过v$SYSMETRIC、AWR报告了解整体压力避免单条SQL优化完被其他会话拖累。5.2 第二步查看当前实际执行计划用DBMS_XPLAN.DISPLAY_CURSOR拿当前实际计划并指定ALLSTATS LASTSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id_替换, 0, ALLSTATS LAST));重点看实际行数、逻辑读、物理读、Starts这几列。如果Starts很大比如内层表被循环访问了几十万次配合看连接方式是不是嵌套循环、驱动表是否选择错误。如果Rows和A-Rows偏差超过两个数量级统计信息失真问题基本实锤。5.3 第三步定位“最贵”的节点执行计划里每个操作符都有Cost占比Cost(%)但不建议只看Cost综合看A-Time和Buffers更可靠。找出耗时最多、产生逻辑读最多、扫描行数最大的节点它就是性能破绽。常见组合全表扫描 大表 过滤条件选择性好基本就是索引缺失或索引失效。NESTED LOOPS 内层表Starts指数级增长驱动表选择错误或者内层表索引不佳。SORT 大行数 temp disk空间溢出排序内存不足考虑增大PGA或减少排序数据。HASH JOIN 内存不足溢出尝试调整PGA策略或增大优化器对驱动表的基数估算准确度。5.4 第四步选择优化手段并验证确认了瓶颈之后优化手段的选择也是有优先级的统计信息刷新成本最低、收益可能最大。执行DBMS_STATS.GATHER_TABLE_STATS刷新相关表和索引很多执行计划问题就是因为统计信息过期引起的。索引优化如果过滤条件或连接条件的列没有合适索引考虑建立索引或组合索引。组合索引的列顺序要遵循“等值列优先、范围列其次”并覆盖高频过滤条件。SQL改写调整表连接顺序、改写子查询为JOIN、拆解大SQL、改写UNION ALL位置等。注意改写必须保证逻辑等价建议在测试环境用数据对比验证。Hints在复杂场景下使用合理的Hints如LEADING、USE_NL、USE_HASH、INDEX等强制指定执行路径这是最后手段因为Hints写死之后不利于后续数据变化的适应性。固定执行计划SQL Plan Baseline可固定已有计划SQL Profile可微调计划。适合核心报表或EBS成本计算这类“数据波动大但是SQL固定”的场景。每做一步改动都重新抓取执行计划并对比前后的A-Time、逻辑读和运行时长用数据说话而不是“我感觉快了”。5.5 排查过程中的常用SQL几个曾经帮我搞定问题的脚本打不开AWR或者不想去EM里找的时候这几条SQL在排查现场非常有用-- 查看当前正在执行的SQL详细信息 SELECT sql_id, sql_text, elapsed_time, buffer_gets, disk_reads, executions FROM v$sql WHERE sql_text LIKE %你要找的特征%; -- 查看某个SQL的历史执行计划版本 SELECT plan_hash_value, COUNT(*), AVG(elapsed_time) FROM v$sql WHERE sql_id 目标sql_id GROUP BY plan_hash_value; -- 查看高消耗SQL Top N SELECT * FROM ( SELECT sql_id, ROUND(elapsed_time/1000000,2) elap_sec, ROUND(cpu_time/1000000,2) cpu_sec, buffer_gets, disk_reads, RANK() OVER (ORDER BY buffer_gets DESC) rn FROM v$sqlstat ) WHERE rn 10;这些SQL不复杂但能快速定位问题SQL和它的执行计划变化历史特别是Plan Hash Value的变化能告诉你执行计划突然变差的时间点配合时间线分析原因非常有价值。6. 常见执行计划问题与排查技巧实录6.1 统计信息失真最普遍的性能陷阱执行计划异常的幕后黑手统计信息失真至少占一半。典型表现SQL在测试环境毫秒级生产环境秒级执行计划中某个表预估返回100行实际返回100万行同样的SQL在数据量不同的库里表现差异极大。应对方式很简单但容易被忽视定期收集统计信息关键表要用DBMS_STATS的自动采样策略大表考虑增量统计或直方图策略。生产核心表变更大批量导入、删除、合并后必须刷新统计信息。另外ALTER TABLE xxx COMPUTE STATISTICS这种老语法虽然还能用但建议改用DBMS_STATS功能更全。6.2 隐式类型转换导致索引失效典型的坑是索引列是VARCHAR2类型查询条件传了数字类型Oracle会隐式把索引列转换成数字结果索引键被函数包裹索引失效走了全表扫描。执行计划里如果看到SQL文本的谓词部分出现TO_NUMBER(COLUMN)VALUE类似的表达式就要警惕隐式转换。另外TO_DATE传字符串时如果格式不对或者日期列和字符串比较也会出现类似情况。我之前遇到过一个案例某月报表突然变慢原因是开发在条件里写了to_char(order_date,yyyy-mm-dd)2024-01-15索引列被函数包裹只能全表扫描。改成order_date to_date(2024-01-15 00:00:00,yyyy-mm-dd hh24:mi:ss)后恢复正常。6.3 绑定变量窥探与直方图的冲突Oracle对绑定变量会做“窥探”Bind Peeking即在第一次硬解析时使用当时的绑定变量值来选择执行计划之后软重用该计划。如果数据分布不均匀且存在直方图绑定变量第一次的值影响极大。例如一个状态字段STATUS分布是99%的“N”和1%的“Y”SQL条件是WHERE status:1。如果第一次解析时的值是“Y”优化器可能选择索引访问之后传入“N”仍然复用这个计划性能就会奇差。解决手段包括使用绑定变量值的扩展统计Extended Statistics、改用SQL Plan Management固定计划或者把这类SQL改写为文本常量如果是应用可控场景。6.4 分页SQL的执行计划优化Oracle分页最常用的写法是ROWNUM嵌套或者Oracle 12c后的OFFSET FETCH。分页SQL的关键问题是“总行数很大但每页数据很少”时优化器仍然可能排序全量数据。理想情况是利用索引消除排序或者将排序下推到分页内部。处理分页SQL时我一般先看执行计划里有没有SORT ORDER BY对全量数据排序。如果有且页大小稳定考虑如下方案一是用索引列排序并加OFFSET FETCH配合FIRST ROWS(n)提示二是把“取第N页”改成“取上一页最后一条的游标条件”Keyset Pagination避免重新扫描之前的所有页。后一种改法在处理百万级数据分页时效果是数量级的提升。6.5 清理监听日志与WIP非标工单的交叉提醒这里提一下搜热词里出现的“Oracle 10g清理监听日志”看上去和执行计划无关但实际运维中监听日志文件过大时listener.log无限制增长会导致监听进程变慢间接引起数据库连接时间变长、应用端延迟增加。排查慢SQL时如果发现连接获取阶段就耗时严重顺手检查监听日志大小是一个值得养成的好习惯。可以用ALTER SYSTEM SET LISTENER_LOG_DIRECTORY参数指定目录或者定期用truncate方式清理日志避免直接删文件导致监听文件句柄失效。6.6 执行计划分析速查表问题现象可能的执行计划特征优先排查方向SQL突然变慢但业务没变计划Hash Value发生变化统计信息是否过期是否出现新索引条件过滤很强仍走全表扫描TABLE ACCESS FULL索引是否存在是否隐式转换是否统计信息缺失表关联极慢NESTED LOOPS循环次数爆炸驱动表选择、被驱动表索引、连接顺序大批量排序溢出SORT操作伴随“TEMP SPILL”PGA内存、排序数据量、索引消除排序临时表空间暴涨HASH JOIN或SORT产生大量磁盘写入内存参数、中间结果集大小、SQL改写视图嵌套查询性能差多层视图解析后基数估算偏差大拆解视图、直接关联底层表并行度异常执行计划出现PX操作符但并发数异常表并行度属性、资源管理器限制7. 一些执行计划之外的调优心得看见执行计划、看懂执行计划、会用执行计划这三步做完基本能解决日常80%的SQL性能问题。剩下那20%就更复杂了涉及到系统架构、应用逻辑、硬件资源等层面这些不是执行计划一个工具能搞定的。我个人在实际优化工作中的体会是执行计划不是终点而是起点。拿到一个异常计划不要急着改SQL先问自己几个问题——统计信息新不新有没有索引能满足这次查询最耗时的那个步骤是否本来可以避免这个SQL有没有必要查这么多列很多时候用执行计划做“减法”比做“加法”更有效——减少不必要的扫描、减少不必要的排序、减少不必要的回表往往比“加一个索引”更接近问题的本质。还有一些习惯值得推荐每次重要SQL优化完把优化前后的执行计划、运行时长、逻辑读数据记录下来。别嫌麻烦半年之后你回看这些记录会发现自己对执行计划的理解深化了很多。特别是EBS里那种几千行的SQL一次优化后的基线和后续变异对比完全靠记忆根本靠不住必须有文档或脚本固化。如果在读这篇文章的朋友正被某个慢SQL折磨我的建议是别慌先抓执行计划找到最大的那一个瓶颈节点解决掉它再找下一个。性能优化从来不是一步到位的艺术而是一点点逼近物理极限的过程。执行计划分析这门手艺多做几个真实案例比读十本书都管用——祝顺手。