简介:Oracle数据库性能优化是保障系统稳定高效运行的核心环节,尤其适合大数据量和高并发的业务环境。文档系统梳理了Oracle数据库调优的主要方面,面向数据库管理员、开发人员及相关运维工程师,帮助读者建立从内存配置到SQL写法再到系统层调优的完整知识框架。压缩包内为单个PDF文档,整体仅128KB,内容精炼便携,方便随时查阅;当前已有1500人学习下载。内容重点包括:数据库服务器SGA内存调整,讲解共享池、数据缓冲区、日志缓冲区的配置原理,并给出系统内存为1G时共享池建议150M-200M等参考数值;SQL语句优化部分,说明基于规则的优化器中驱动表的选择、WHERE条件过滤顺序、避免SELECT *、优先用WHERE代替HAVING等技巧;此外还覆盖索引建立、分区策略、回滚段管理、执行计划控制,以及操作系统参数和网络性能调优。整体而言,可作为DBA日常调优和排查性能瓶颈的系统参考。
1. Oracle 数据库性能优化:为什么先动 SGA 再改 SQL
Oracle 数据库的性能问题,十有八九最后落在两个点上:内存里放不下,SQL 写得不划算。这份《Oracle数据库性能优化》PDF 就是围绕这两件事展开的——前面讲系统全局区(SGA)里共享池和数据缓冲区怎么取值,后面讲驱动表、WHERE 顺序、SELECT * 这些改写技巧对解析成本的影响。我拿着线上库 CPU 高、慢查询扎堆的现场去对照读,读完最大的收获是:优化是有先后顺序的,先定内存边界,再改 SQL,最后才轮到操作系统和网络。它适合正在带 Oracle 库的 DBA、运维转数据库方向的工程师,也适合写 SQL 老觉得执行计划不听话的应用开发——案例是老了些,但今天的坑大多还能在这份材料里找到原型。
2. SGA 调整:共享池 150–200M 起步,缓冲区命中率别只看数字
数据库服务器内存参数调整,是 PDF 里点名的第一个调优方向。Oracle 实例启动后,SGA 会按参数把内存分给固定区域、共享池、数据缓冲区、日志缓冲区等几个部分。应用发来的每条 SQL、每次数据读取,都要经过这几块内存,所以说 SGA 是性能的第一道闸门。
2.1 共享池:先看解析过程,再定参数
共享池存放最近使用过的 SQL、PL/SQL 代码和数据字典里的元数据。它由库高速缓存(Library Cache)和数据字典缓冲区(Data Dictionary Cache)组成。Oracle 收到一条 SQL,先做语法分析、权限确认,再交给优化器生成执行计划,最后才真正执行。这个过程在共享池足够大的时候可以被跳过——相同文本的 SQL 再次进来,直接命中缓存的执行计划,省掉重复解析的时间。
这就是为什么共享池是“对性能影响显著”的首选调整对象。PDF 给了一条很实用的经验值:系统内存 1G 时,共享池设 150M–200M;内存每增加 1G,它的值加 100M;但上限不要超过 500M。
这条经验我建议直接抄进自己的优化手册。它的依据是 LRU 算法:共享池内部用最近最少使用算法淘汰旧条目,缓存命中率与池子大小不是线性关系,超过某个临界点后,Oracle 维护内部结构(free lists、lru chains)的开销反而比省下来的解析时间更贵。另一个风险是共享池偏大会挤压操作系统的可用内存,Oracle 进程一旦被换出到虚拟内存,系统整体响应会断崖式下跌。
在具体操作前,先把当前占用和命中情况摸清楚:
-- 查看共享池各区域占用,按字节倒序排 SELECT pool, name, ROUND(bytes / 1024 / 1024, 2) MB FROM v$sgastat WHERE pool = 'shared pool' ORDER BY bytes DESC; -- 查看库高速缓存命中情况 SELECT namespace, gets, gethitratio, pins, pinhitratio FROM v$librarycache;第一段 SQL 用来确认共享池里谁是“大户”,正常情况下 library cache 和 sql area 占大头,如果 free memory 长期很小,说明池子偏紧。第二段 SQL 中,gets 是解析请求次数,gethitratio 表示该 namespace 下直接命中缓存的比率,pins 是执行阶段引用次数,pinhitratio 是执行期命中率。这里没有统一的“健康线”,但 gethitratio 低于 90% 时,值得考虑加大 shared_pool_size。
2.2 数据缓冲区:物理读与逻辑读的换算
数据缓冲区(DB Buffer Cache)缓存从磁盘读出来的数据块。理解它很简单:缓冲块越大,第二次访问同一块数据就不用回磁盘,直接命中内存。物理读少一批,响应时间自然下来。
不过,缓冲区也服从边际收益递减。假设一个 20G 的库,缓存 2G 时物理读明显下降,但从 2G 加到 4G,收益可能只有前一段的五分之一。PDF 原文强调“缓冲区越大,存放的共享数据就越多”,但没提醒你“大过头会挤压操作系统”。实际运维中,我一般把 SGA 总和控制在物理内存的 60%–70%,剩下留给 PGA 和操作系统文件缓存。
想看当前全库的缓冲区命中率,可以用这条经典 SQL:
-- 缓冲区命中率,注意这只是总体参考值 SELECT 1 - (phy.value / (cur.value + con.value)) AS buffer_hit_ratio FROM v$sysstat phy, v$sysstat cur, v$sysstat con WHERE phy.name = 'physical reads' AND cur.name = 'db block gets' AND con.name = 'consistent gets';公式的含义是:物理读次数除以逻辑读总数,再用 1 减去,得到命中率。db block gets 是当前模式读,consistent gets 是一致性读,两者相加是逻辑读总量。这条语句放在监控脚本里可以,但别把它当成调优结论的唯一下判断依据——一个全表扫描如果数据全在内存里,命中率照样很高,可 CPU 一样被扫描动作吃满。
2.3 用 v$db_cache_advice 判断加不加缓存
比较稳妥的做法,是让 Oracle 自己算一笔账。v$db_cache_advice 会为不同缓存大小预测物理读变化,这是调优时少有的“先看预测再动手”手段:
-- 查看不同缓存大小对应的物理读预测 SELECT size_for_estimate, estd_physical_read_factor, estd_physical_reads FROM v$db_cache_advice WHERE name = 'DEFAULT' AND block_size = 8192 ORDER BY size_for_estimate;size_for_estimate 是假设的缓存大小,estd_physical_read_factor 是相对当前值的物理读比例,estd_physical_reads 是估算出的物理读次数。如果 size_for_estimate 从 1G 涨到 2G 时 factor 从 1.0 掉到 0.6,说明加大缓存回报不错;如果 2G 到 3G 只降到 0.55,说明增量收益已经很薄。我一般选曲线“膝盖”位置,而不是追 100% 命中。
2.4 参数落地:alter system 与 scope 取舍
跑完诊断,真正改参数时,先确认实例是从 spfile 启动还是 pfile 启动,这决定了你改完能不能持久:
-- 确认参数文件方式,VALUE 不为空说明用了 spfile SHOW PARAMETER spfile; -- 查看当前相关参数 SELECT name, value, isdefault FROM v$parameter WHERE name IN ('shared_pool_size', 'db_cache_size', 'log_buffer');如果要从手动管理切到自动管理,常见做法是先定 sga_target,让 Oracle 自动调配各池;如果库上跑的 SQL 特征非常稳定,也可以继续用手动值:
-- 修改共享池,希望立即生效并写入 spfile,用 SCOPE=BOTH ALTER SYSTEM SET shared_pool_size = 300M SCOPE = BOTH; -- 修改数据缓冲区,接受重启生效,用 SCOPE=SPFILE ALTER SYSTEM SET db_cache_size = 1536M SCOPE = SPFILE;SCOPE 三个取值要分清楚:MEMORY 只改当前实例,重启即丢;SPFILE 只写服务器参数文件,要重启才生效;BOTH 是当前值和文件都改。注意静态参数不能带 BOTH,比如 log_buffer 这类需要重启的参数,带 SCOPE=BOTH 会直接报错。日志缓冲区一般不建议手工乱调,交给 Oracle 默认值在绝大多数场景下更稳。
提示:改 SGA 相关的关键参数前,先执行
CREATE PFILE='/tmp/pfile_backup.ora' FROM SPFILE;留一份后悔药。线上改参最怕改完想回头,手里却没有原始配置。
参数调整不是一锤子买卖。每次只动一个参数,观察 5 到 10 分钟,比较 AWR 报告里 physical reads、logical reads、db time 这三个指标的变化,再决定下一步。一次改三个参数,出了问题你根本不知道是哪个惹的祸。
3. SQL 改写:驱动表、WHERE 顺序、SELECT * 与 HAVING 的取舍
应用代码最终都归结为数据库里的 SQL 执行。同样的业务,SQL 写法不同,执行成本可能差出几十倍。PDF 里对 SQL 优化的讲解,是基于规则优化器(RBO)时代的习惯,核心就四件事:驱动表放哪、WHERE 条件怎么排序、要不要写 SELECT *、WHERE 和 HAVING 怎么分工。这些结论放到现在,部分已经失效,但思考方式完全能平移。
3.1 驱动表:from 子句的解析顺序与选择
在基于规则的优化器里,Oracle 对 FROM 子句按从右到左解析,排在最后的表最先被处理,这张表就是驱动表。驱动表决定连接的起点:它产生多少行,后续表就要被访问多少次。
PDF 给了一条硬规则:选择记录条数少的表作为驱动表,放在 FROM 子句最后。FROM 里有 3 张以上表时,把连接其他表的“交叉表”作为驱动表。
这条规矩背后的逻辑很好懂。以最常见的嵌套循环连接为例,驱动表是外层循环,外层行数越少,内层表被探访的次数就越少。如果把一张百万级大表放最后当驱动,外层就是百万次循环,哪怕每次探访很快,总耗时也扛不住。
-- 按 RBO 习惯:小表 dept 放 FROM 最后,先被处理 SELECT a.dept_name, COUNT(b.emp_id) AS emp_cnt FROM emp b, dept a WHERE a.dept_id = b.dept_id GROUP BY a.dept_name;这里 emp 是大表、dept 是小表,把 dept 放在最后,它先进入内存作为外层,再去逐行匹配 emp,省掉重复扫描大表的开销。
需要特别提醒:这条规则只在 RBO 下成立。Oracle 10g 之后默认是基于代价的优化器(CBO),驱动表由统计信息和成本计算决定,你调 FROM 顺序它不一定理你。跨表关联时我一般先看执行计划里的 NESTED LOOPS 哪一侧是 outer,再判断驱动表选得对不对,而不是盲目相信书写顺序。
3.2 WHERE 子句:自下而上解析与高选择性条件
PDF 明确写:WHERE 子句的执行顺序是自下而上。这意味着最后写的条件最先执行,所以应该把能过滤掉大量数据的条件放在 WHERE 的最后。
CBO 环境下,优化器会做谓词评估顺序的调整,书写顺序不代表执行顺序,但“尽早过滤”这个原则永远不会过时。相比纠结条件先后,更关键的是别让索引列在过滤前被函数包一层:
-- 反面:对 hire_date 做函数转换,索引失效,还得先算出所有年份再筛 SELECT emp_id, emp_name FROM emp WHERE TO_CHAR(hire_date, 'YYYY') = '2020' AND dept_id = 30; -- 正面:用范围条件命中索引,且过滤能力最强的条件写后面 SELECT emp_id, emp_name FROM emp WHERE dept_id = 30 AND hire_date >= DATE '2020-01-01' AND hire_date < DATE '2021-01-01';两段 SQL 的结果一样,开销完全不同。第一段对 hire_date 套了 TO_CHAR 函数,Oracle 无法直接利用 hire_date 上的索引,只能全表扫描后逐行转换;第二段改成大于等于小于的范围写法,既保留索引可用性,又把查询范围锁死在一年内。这个例子在 PDF 基础上我多改了一步,因为只调顺序不拆函数,十次里有八次还是慢。
3.3 SELECT * 的隐藏成本:不止多查一次数据字典
PDF 说得很直白:SELECT * 虽然写起来简单,但 Oracle 解析星号时需要查询数据字典完成列名转换,比直接写列名多耗时间。这一步在 OLTP 频繁小查询场景下,放大效应很明显。
工程上我还会补两条。一是网络传输:SELECT * 返回所有列,多出的列如果应用根本不用,白白增加客户端和数据库之间的数据传输量。二是 PL/SQL 里的隐患,如果表结构后续加列,SELECT * 的结果集变宽,游标变量或记录类型的赋值可能直接报错,属于典型的线上翻车点:
-- 反例:返回全部列,列变更时游标结构跟着变 SELECT * FROM emp WHERE dept_id = 30; -- 正例:只取需要的列,结构稳定且解析更轻 SELECT emp_id, emp_name, hire_date FROM emp WHERE dept_id = 30;这里没有惊天的理论,纯粹是“能省则省”。一个一天执行几十万次的小查询,省一次数据字典查询和省两列无用传输,累计起来就是肉眼可见的负载差异。
3.4 WHERE 替代 HAVING:过滤时机不同
WHERE 在分组聚合之前执行,HAVING 在分组聚合之后执行。同样一个非聚合条件的过滤,放进 HAVING,意味着所有行都要先分组、先聚合,再把不要的组丢掉;放进 WHERE,则在分组前就把行排除,参与聚合的数据量直接变少。PDF 说的“用 WHERE 代替 HAVING”,适用条件就在这里:
-- 反例:dept_id 是非聚合列,先进组再过滤,白算了一堆没用的分组 SELECT dept_id, COUNT(*) FROM emp GROUP BY dept_id HAVING dept_id = 30; -- 正例:先排除 dept_id 非 30 的行,再分组聚合 SELECT dept_id, COUNT(*) FROM emp WHERE dept_id = 30 GROUP BY dept_id;注意,不是所有 HAVING 都能改成 WHERE。过滤条件如果依赖聚合结果,比如HAVING COUNT(*) > 100,那只能留在 HAVING 里,因为 WHERE 执行时聚合还没算出来。判断标准只有一个:条件是不是针对原始行的。是,就用 WHERE;不是,才用 HAVING。
3.5 用执行计划验证改写效果
改写完不能靠猜,要拿执行计划说话:
EXPLAIN PLAN FOR SELECT emp_id, emp_name FROM emp WHERE dept_id = 30 AND hire_date >= DATE '2020-01-01' AND hire_date < DATE '2021-01-01'; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);重点看两列:Operation 和 Rows。Operation 里出现TABLE ACCESS FULL说明在扫全表,出现INDEX RANGE SCAN说明索引生效;Rows 是优化器估算的行数,驱动表那一步的 Rows 越小,连接成本通常越低。改写前跑一次,改写后跑一次,两张计划对比,谁好谁坏一眼就清楚。别只盯着 Cost 这一个数字,Rows 估算偏差大时,Cost 会骗人。
4. 避坑指南:共享池翻车、命中率错觉与参数改不生效
优化做多了,发现真正耽误时间的不是优化本身,而是没识别出哪些“常见操作”其实是坑。以下五条都是我自己踩过或看同事踩过的,按现象、原因、解决三步拆开写。
4.1 共享池调到 1G 之后,服务器开始“换页式”变慢
现象:某次为了缓存更多 SQL,把 shared_pool_size 从 300M 直接调到 1G,几小时后系统整体变慢,CPU 使用率升高,iowait 明显增长,top 里看到大量 si/so 换页,应用开始出现响应超时。
原因:SGA 总占用超过物理内存剩余量,Linux 把 Oracle 的部分内存段换到 swap。共享内存被换出后,任何一次访问都要先换入,性能断崖式下跌。共享池并不是越大越好,PDF 里那句“最大值不超过 500M”不是随口写的。
解决:立即把 shared_pool_size 调回 500M 以内,SGA 加上 PGA 的总和控制在物理内存 60%–70%。如果内存确实有富余,先确认 HugePages 已启用,再考虑加大缓存,否则越大的 SGA 越容易变成换页重灾区。
4.2 缓冲区命中率 99.9%,查询还是慢
现象:监控面板上 buffer hit ratio 高到 99.9%,应用却持续报慢查询。查 v$sql 后发现某条 SQL 每次执行要逻辑读几十万个块,执行频次又高,CPU 全部耗在内存遍历上。
原因:命中率高只代表物理读少,不代表逻辑读少。全表扫描数据全在内存时命中率照样漂亮,但扫描动作本身的 CPU 开销一点没减。把命中率当健康指标,是调优里最典型的错觉之一。
解决:丢弃全局命中率,改用 v$sql 看单条 SQL 的逻辑读、物理读和执行次数:
-- 按逻辑读排序,找出真正的“大户” SELECT sql_id, sql_text, executions, logical_reads, physical_reads, ROUND(elapsed_time / 1000000, 2) AS elapsed_sec FROM v$sql WHERE executions > 0 ORDER BY logical_reads DESC FETCH FIRST 10 ROWS ONLY;这里 logical_reads 是内存读块数,physical_reads 是磁盘读块数,elapsed_sec 是累计耗时。三者结合看,才能定位“命中率没问题但慢”的真凶。
4.3 把 RBO 的驱动表规则硬套到 CBO 上
现象:按 PDF 旧规则,把记录少的小表放到 FROM 最后,等着看执行计划变成嵌套循环,结果计划纹丝不动,有时甚至变得更差。
原因:10g 之后默认 CBO,执行计划由统计信息和代价决定,FROM 书写顺序对连接顺序几乎没影响。PDF 原文写得很清楚——“在基于规则的优化器中”,适用边界只到 RBO。拿旧规则指挥新优化器,等于往方向盘上贴了张过期的地图。
解决:先确认优化器模式,SHOW PARAMETER optimizer_mode,如果显示 ALL_ROWS 或 FIRST_ROWS,就老老实实按 CBO 思路走:保证统计信息新鲜,必要时用 hint 明确指定连接顺序,比如LEADING(dept) USE_NL(emp),而不是改 FROM 顺序期待优化器听你的。
4.4 改完参数看着生效,重启后打回原形
现象:执行ALTER SYSTEM SET shared_pool_size = 300M;后,v$parameter 里确实变成了 300M,欣欣然下班。第二天数据库例行重启,参数又变回 150M。
原因:实例从 pfile 启动时,ALTER SYSTEM 的默认行为只改内存,不写文件。pfile 是文本文件,Oracle 不会帮你回写。从 spfile 启动时,默认行为才是既改内存又写 spfile。问题出在“没确认启动方式”。
解决:改动前先执行:
SHOW PARAMETER spfile;如果 VALUE 为空,说明走的是 pfile,先创建 spfile:
CREATE SPFILE FROM PFILE; ALTER SYSTEM SET shared_pool_size = 300M SCOPE = SPFILE;从那以后我养成了习惯:所有参数变更语句后面都显式写 SCOPE,绝不依赖默认值。
4.5 统计信息过期,执行计划“漂移”
现象:同一条 SQL,昨天走索引一百毫秒内返回,今天突然全表扫描,耗时飙到五秒。SQL 文本没动,表结构没动,应用没发新版本。
原因:表的数据量和数据分布变了,但统计信息还是老的,CBO 按过期的统计信息算出的成本失真,做出了全表扫描的错误选择。数据倾斜越大,这类漂移越常见。
解决:定期收集统计信息,关注数据变化量大的表:
-- 查看哪些表被修改过但统计信息没更新 SELECT table_name, inserts, updates, deletes FROM dba_tab_modifications ORDER BY inserts + updates + deletes DESC; -- 手动收集关键表统计信息 EXEC DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT', tabname => 'EMP', cascade => TRUE);生产环境建议在维护窗口统一收集,核心大表单独安排频率。如果历史上执行计划已经漂移过多次,还可以考虑用 SQL Plan Management(SPM)把稳定计划固定下来,给优化器加一道保险。
5. 操作系统与网络配合:大页、SQL*Net 等待与 AWR 基线
数据库的瓶颈不一定都在数据库内部。PDF 在引言里点过操作系统参数和网络性能调优这两个方向,正文虽然只展开了前两项,但真正落地时,OS 和网络层不配合,SGA 调得再好也会被外部因素拖住。
5.1 Linux 大页(HugePages)与 SGA 的配合
Linux 默认内存页是 4KB,SGA 动辄几 GB,意味着操作系统要维护上百万条页表项。CPU 的 TLB 缓存有限,页表项一多,TLB 命中率下降,内存访问变慢。更麻烦的是,SGA 这类共享内存段在系统内存压力大时可能被换出到磁盘,触发灾难性的 swap 抖动。
常见做法是给 Oracle 配置 HugePages,让共享内存段使用 2MB 甚至 1GB 的大页。先看当前情况:
# 检查大页配置和实际使用 grep -i hugepages /proc/meminfo重点看 HugePages_Total 和 HugePages_Free。如果 Total 为 0,说明没有启用。设置大致分三步:先固定 SGA 相关参数,再按 SGA 大小计算页数,最后写入系统配置:
# 假设 SGA 为 8G,使用 2MB 大页,约需 4096 页,留 10% 余量 sysctl -w vm.nr_hugepages=4600 # 将设置写入 /etc/sysctl.conf 使其持久化 echo 'vm.nr_hugepages=4600' >> /etc/sysctl.conf # 重启 Oracle 实例后确认共享内存段是否落在大页上 ipcs -m | grep oracle一个必须先说的前提:Oracle 的自动内存管理(memory_target)与 HugePages 不兼容。启用大页前,要把 memory_target 设为 0,改用 sga_target 和 pga_aggregate_target 手动管理。否则实例可能启动失败,或者看似启动了,共享内存段根本没进大页。我踩过一次:配好大页后 Oracle 起不来,报错信息含糊,最后排查到是 memory_target 残留在 spfile 里。
5.2 网络层:SQL*Net 等待不等于网络故障
AWR 报告里如果看到 SQLNet message from client 排进 Top Events,先别急着下“网络有问题”的结论。这个事件大部分时间表示客户端在思考或发呆,是空闲等待。真正要警惕的是 SQLNet roundtrips to/from client 这类和往返次数强相关的事件,它说明应用和数据库之间的交互次数过多。
我常用的排查方法是“两端对照”:在应用服务器上和数据库服务器上分别跑同一条 SQL,对比耗时。如果 sqlplus 本机执行也慢,问题在 SQL 或存储;如果本机快、应用慢,才往网络和应用层方向查。应用侧能做的优化是减少往返——把循环里一条条执行的 SQL 改成批量提交,或者把多次查询合并成一条集合查询。网络参数层面,常见做法是调整服务端和客户端之间的 TCP 超时与存活探测,但这解决的是断连和僵死连接,解决不了“交互次数太多”的根子问题。
5.3 建立监控基线:AWR 快照与报告阅读
优化改完,怎么知道有没有效果?AWR 是最省事的工具。它可以手工打快照,也可以设成固定间隔自动打:
-- 手动创建一次快照,做变更前的基线记录 EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;生成报告的标准动作是在 sqlplus 里调用 awrrpt 脚本:
sqlplus / as sysdba @?/rdbms/admin/awrrpt.sql按提示选择起始快照和结束快照,再选 text 或 html 格式。报告生成后,我一般只看三个区块:Top 10 Foreground Events by Total Wait Time,判断瓶颈是 CPU、I/O 还是网络;SQL ordered by Elapsed Time,定位最贵的 SQL;Instance Activity 里的 physical reads、logical reads 和 db time,对比优化前后曲线。
变更前打一次快照,变更后稳定运行几小时再打一次,两次报告放一起比,效果好过任何口头的“好像快了点”。没有基线就谈优化效果,都是凭感觉。
6. 优化落到日常:留快照、比执行计划、记台账
优化不是一次性工程,它更像给数据库做长期健康管理。我自己的习惯是每次优化动作都走一套固定流程:先记录环境和基线,再定位最贵的 SQL,然后用执行计划对比验证,最后把结果写进台账。
具体做法是固定三步。第一步,做变更前快照,并把这几个值记下来:SGA 各池大小、PGA 大小、物理读和逻辑读数量、最贵的前三条 SQL。第二步,对目标 SQL 做改写,改完马上做一对对比实验:
-- 改写前:打开详细统计后执行原 SQL ALTER SESSION SET statistics_level = ALL; SELECT /* baseline */ emp_name, dept_name FROM emp e JOIN dept d ON e.dept_id = d.dept_id WHERE e.hire_date >= DATE '2020-01-01'; -- 查看刚才这条 SQL 的真实执行统计 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));记下输出里的 Rows、Time、A-Rows 和 Buffers,再把改写后的 SQL 用同样流程跑一遍,看 A-Rows 是否一致、Rows 估算是否更准、Elapsed Time 是否下降。执行计划显示的行数和实际返回行数差距越大,说明统计信息越失真,这时改 SQL 不如先收集统计信息。
第三步,把结果写进台账。我习惯用一张简单的表:日期、SQL_ID、改动内容、改动前耗时、改动后耗时、执行计划变化。积累了半年的台账回头翻,哪些改法在这个业务场景下稳定有效,哪些换个表就不灵了,一目了然。
这份 PDF 里最有价值的东西,不是那几个参数数字,而是“优化前先分主次”的思路。从那以后,我每次遇到性能问题都强制自己走一遍流程:先拍快照记基线,再查 v$sql 找最贵的,再借助执行计划改 SQL,改完用 ALLSTATS 对比验证。这套动作下来,省掉了很多被“感觉”带偏的时间,也希望帮到你。
本文还有配套的精品资源,点击获取