news 2026/10/3 1:35:22

Oracle性能优化实战:先诊断再调参,AWR+SQL优化让数据库变快

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Oracle性能优化实战:先诊断再调参,AWR+SQL优化让数据库变快

简介:一份面向数据库运维、开发与架构师人群的Oracle性能优化PDF文档,围绕大数据量、高并发场景下的调优难题,从数据库服务器内存参数调整与SQL语句优化两条主线切入,并延展至索引、分区、回滚段、执行计划控制以及操作系统、网络层面的性能影响因素。全篇为1个PDF文件,压缩包仅128KB,内容紧凑,适合需要快速抓住调优重点的工程师阅读。文档重点讲解SGA内存参数设置:共享池、数据缓冲区与日志缓冲区的合理取值与调整原则,强调过大或过小都可能导致性能下降,并给出系统内存为1G时共享池建议150M-200M、最高不超过500M的具体经验值;同时给出基于规则优化器下驱动表选择、WHERE条件书写顺序、避免SELECT *、用WHERE替代HAVING等可落地的SQL写法。索引、分区策略、回滚段管理与执行计划引导部分,可帮助读者从存储机制和执行路径层面进一步消除瓶颈,整体形成从内存、语句到系统环境的优化思路。目前已有1500人学习下载,适合需要系统梳理Oracle调优要点的初中级工程师参考。

1. 遇到Oracle变慢先别调参:先把时间耗在哪搞清楚

遇到Oracle数据库变慢,很多人的第一反应是加内存、改参数,结果钱花了库还是慢。我在生产环境处理过不少这类问题,得出的经验是:oracle数据库性能优化真正的第一步不是调参,而是先回答“时间到底耗在哪”。这份主题对应的是从诊断、参数、SQL到例行巡检的一整套可落地路径,适合被慢SQL折磨的开发、需要扛住业务高峰的DBA,以及接手一个没人维护的旧库想理清头绪的运维。

优化不是玄学,是把排队问题一个个解开。数据库变慢本质上就是两类事:要么某个等待特别长,要么某类SQL消耗了绝大多数资源。下面按我实际排查的顺序展开,每一步都能直接照着做,也能帮你判断别人给的调优方案靠不靠谱。

2. 拿到一份慢库:先做诊断三件事,别急着改参数

接手一个生产库的第一天,我最忌讳的就是直接翻出ALTER SYSTEM改参数。Oracle性能优化不是撞运气,而是先回答三个问题:瓶颈在CPU还是IO、哪类等待事件在排队、Top SQL是谁。这三件事做完,方案基本就出来了。

2.1 用AWR报告定位瓶颈:先看哪几个段

AWR(Automatic Workload Repository)是Oracle内置的体检报告,默认每小时自动生成一次快照,并保留8天。平时觉得库慢,可以先手动生成一份当前时段的AWR报告,用系统脚本就行。

-- 以DBA身份登录,调用awrrpt脚本生成报告 SQL> @?/rdbms/admin/awrrpt.sql -- 按提示依次输入:报告类型(html/text)、过去天数、起始快照号、结束快照号 -- 生成的HTML报告可以直接用浏览器打开,便于定位瓶颈

逻辑说明:@?/rdbms/admin/awrrpt.sql里的?指$ORACLE_HOME,脚本会读取dba_hist_snapshot中的历史快照,把你指定的两个快照之间的负载汇总成报告。报告类型选HTML是因为它自带表格和链接,点起来比文本方便得多。

AWR报告很长,不要从头翻到尾。我一般只看四个段,按顺序记在下面:

AWR报告段落看什么异常信号
Reports SummaryDB Time与Elapsed TimeDB Time接近Elapsed Time说明CPU满载
Top 10 Foreground Events by Total Wait Time等待事件占比单一事件占比超过30%要深挖
SQL ordered by Elapsed Time最耗时的SQL清单前几条占DB Time的70%以上
Instance Activity每秒事务数、解析次数、物理读每秒物理读持续几千次通常有大问题

提示:生产环境生成AWR本身有轻微开销,不要在业务高峰反复跑。一份报告覆盖1到2小时就够了,跨度太长反而看不出瞬时的峰值问题。

如果发现DB Time远大于Elapsed Time,说明CPU已经被吃满,核心矛盾在SQL或内部竞争;如果DB Time很小但用户还是觉得慢,问题更可能在应用层或网络,不在数据库本身。这个判断决定了你后面要不要动数据库,避免给Oracle乱扣帽子。

2.2 等待事件与Top SQL:把排队翻译成“改哪里”

AWR的等待事件部分是最直接的诊断入口。我常跟团队说,等待事件就是数据库在跟你说“我卡在哪个环节了”,不用猜。常见的几类要记住:db file sequential read代表单块读,多半是索引回表太勤;db file scattered read代表多块读,多为全表扫描;log file sync表示提交太慢,跟写日志和磁盘有关;enq: TX - row lock contention则是行锁竞争,通常指向并发事务打同一行。

只看AWR里的汇总还不够,实时排查慢SQL要用v$sql。这个视图里存着当前实例里所有仍在内存中的SQL执行统计,按累计耗时排序就能抓到“大胃王”。

-- 找出累计执行时间最长的前10条SQL(12c及以上) SELECT sql_id, executions, ROUND(elapsed_time / 1000000, 2) AS elapsed_sec, ROUND(cpu_time / 1000000, 2) AS cpu_sec, buffer_gets, disk_reads FROM v$sql WHERE elapsed_time > 1000000 ORDER BY elapsed_time DESC FETCH FIRST 10 ROWS ONLY;

参数说明:elapsed_time是这条SQL从解析到执行结束的累计微秒数,executions是执行次数。如果一条SQL执行了十万次,每次0.01秒,累计也能排到前面,但它并不是最值得优化的对象。我一般会再用elapsed_time / executions算单次平均耗时,优先处理“单次又慢、执行次数又多”的SQL。

11g及更早版本没有FETCH FIRST,改成WHERE ROWNUM <= 10包一层子查询即可。拿到sql_id后,可以直接用它去生成执行计划,也可以到AWR报告的SQL段里查它的历史表现。

2.3 基线快照留好:优化前后对比的后悔药

很多人一上来就改,改完发现更慢,却拿不出“改之前到底什么样”的证据。所以我接手任何库,第一件事就是打快照、做基线。这是性价比最高的动作,相当于给自己留一颗后悔药。

-- 优化前手动打一个快照 EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT(); -- 把100到110号快照固定为基线 EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_BASELINE( start_snap_id => 100, end_snap_id => 110, baseline_name => 'BEFORE_OPT_20250120');

逻辑说明:CREATE_SNAPSHOT是主动触发一次负载采集,采完才能对比。CREATE_BASELINE把指定范围内的快照包成一条基线,日后可以在AWR页签里把当前负载同基线并排对比,直接看DB Time和Top SQL的变化。

参数说明:start_snap_id、end_snap_id要取自dba_hist_snapshot,建议前后覆盖一个完整的业务周期;baseline_name命名要带日期和目的,我习惯写成BEFORE_OPT_日期这种格式,避免三个月后认不出来。

优化做完后,再打一次快照、生成对比报告。如果DB Time降了、Top SQL的单次耗时降了,说明优化有效;如果没降,至少知道要回退到基线状态,而不是继续盲目加参数。这个习惯在夜间批处理优化里尤其有用,因为批处理的性能波动往往要到第二天白天才会暴露。

3. 参数与内存调优:SGA/PGA怎么给才算合适

诊断做完,如果等待事件指向内存类问题,或者Top SQL显示共享池命中率异常,才轮到参数调优。Oracle的内存参数不是越大越好,给多了反而可能引起内存换页和频繁的GC式内部操作,这是新手最容易踩的坑。

3.1 SGA与PGA的配比逻辑:先从命中率说起

SGA包含数据库缓冲区、共享池、大池、Redo日志缓冲区等,PGA则服务于排序、哈希连接和PL/SQL运行。OLTP和OLAP的配比完全不同,别拿一套公式套所有库。

-- 查看当前SGA/PGA配置 SHOW PARAMETER sga_target; SHOW PARAMETER pga_aggregate_target; -- 直接看关键计数器 SELECT name, value FROM v$sysstat WHERE name IN ('physical reads', 'consistent gets', 'db block gets');

逻辑说明:physical reads是实际从磁盘读的块数,consistent gets与db block gets是内存中的逻辑读。命中率的大致算法是1 - physical_reads / (consistent_gets + db_block_gets),AWR报告里会直接给出,但我不建议把它当唯一指标。很多走索引的SQL命中率极高,依然慢,因为慢在单块读的物理IO次数太多。

我常用的配比参考:

物理内存典型SGA_TARGET典型PGA_AGGREGATE_TARGET
32G12G~16G4G~6G
64G24G~32G8G~12G
128G64G左右16G左右

SGA和PGA加起来不要超过物理内存的70%到80%,剩下留给操作系统文件缓存和网络栈。如果跑的是高并发OLTP,PGA就可以往下压;如果是报表分析型负载,PGA要适当多给。血泪经验是:PGA给少了,排序会落临时表空间,慢起来比磁盘读还可怕;给多了又会挤占SGA,导致缓冲命中率下降。

3.2 自动内存管理与手动调整的取舍

11g引入的MEMORY_TARGET让Oracle自动调配SGA和PGA,看起来省心,但在生产环境我通常选择半自动或全手动。原因有两个:一是Linux下配置HugePages时,自动内存管理和大页存在相互排斥,启动容易报ORA-27102;二是自动管理在你制定了SGA目标后,并不会主动帮你优化等待事件,出了问题反而更难排查。

-- 关闭自动内存管理,改用半自动(AUM + 手动PGA目标) ALTER SYSTEM SET memory_target=0 SCOPE=SPFILE; ALTER SYSTEM SET sga_target=16G SCOPE=SPFILE; ALTER SYSTEM SET pga_aggregate_target=4G SCOPE=SPFILE; ALTER SYSTEM SET pga_aggregate_limit=8G SCOPE=SPFILE;

参数说明:memory_target=0表示关闭自动内存管理的总开关,这个改动必须在SPFILE里,重启实例生效。sga_target是SGA的上限动态目标,pga_aggregate_target是PGA的软目标,pga_aggregate_limit是PGA的硬上限,可以防止某个极端会话把PGA撑爆。

改完之后检查一下大页配置和锁定限制:

# 查看当前大页分配情况 grep HugePages_Total /proc/meminfo grep HugePages_Free /proc/meminfo

如果HugePages_Free长期很低,说明SGA锁占了大页但没被充分利用,常见原因是shared_pool或buffer cache分配过大。Oracle的SGA要锁在大页里,数量不够时进程启动会直接失败。配置大页是Linux下Oracle调优绕不开的一关,别只看参数好看,落地时要把/etc/security/limits.conf的memlock一起调到位。

注意:改sga_target属于静态参数,要重启实例才生效。重启前一定先备份SPFILE,生成一份PFILE作为启动兜底,避免参数写错后连库都起不来。

3.3 常用性能参数的查看与修改命令

参数调优不是背命令,而是要有自己的“检查单”。我上任何一台生产库,都会先把这几个参数过一遍:

-- 查看核心性能参数 SHOW PARAMETER shared_pool_size; SHOW PARAMETER db_cache_size; SHOW PARAMETER open_cursors; SHOW PARAMETER session_cached_cursors; SHOW PARAMETER cursor_sharing; SHOW PARAMETER optimizer_index_cost_adj;

这些参数里,open_cursors和session_cached_cursors与游标复用直接相关,OLTP库里打开游标过多会导致共享池碎片和解析压力。通常我会把session_cached_cursors调到100到300之间,具体看会话数。

ALTER SYSTEM SET open_cursors=1000 SCOPE=BOTH; ALTER SYSTEM SET session_cached_cursors=200 SCOPE=BOTH;

说明:SCOPE=BOTH表示立即生效并写入SPFILE,但只适用于动态参数。像shared_pool_size这类静态参数,SCOPE只能写SPFILE,等重启生效。optimizer_index_cost_adj我一般不动,除非执行计划已经明显偏向全表扫描而索引应该更快,这个参数会整体缩放索引访问的代价,改不好会让所有SQL的计划乱套。

参数影响生效方式
sga_targetSGA总大小重启
pga_aggregate_target排序/哈希内存动态
pga_aggregate_limitPGA硬上限动态
open_cursors单会话游标上限动态
session_cached_cursors会话游标缓存动态

血的教训是不要一次改多个参数。我见过同事一口气改了六个参数,重启后跑批性能掉了20%,根本不知道是哪条改出来的问题。正确的做法是一次只改一个,观察一个业务周期,用AWR基线对比得出结论后,再动下一个。

4. SQL优化:一条慢SQL从定位到改写的关键步骤

参数没问题、内存也正常,那慢SQL十有八九是执行计划走偏。Oracle性能优化到了这一步,重心从数据库转向SQL本身。一条慢SQL的优化,核心动作就三个:看清真实执行计划、检查索引是否能用上、判断统计信息是否过期。

4.1 执行计划怎么看:从全表扫描到索引选择的判断

执行计划是Oracle对这条SQL的操作蓝图。先解释一条还没跑的SQL,用EXPLAIN PLAN FOR,是新手最容易上手的办法:

EXPLAIN PLAN FOR SELECT order_id, cust_id, amount FROM order_t WHERE cust_id = 100 AND create_time >= DATE'2025-01-01'; -- 展示刚才生成的执行计划 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());

逻辑说明:EXPLAIN PLAN FOR会把计划写入计划表,DBMS_XPLAN.DISPLAY()负责格式化输出。但要注意,它展示的是“估算”计划,实际执行时会受绑定变量窥探、统计信息影响,计划和预算往往对不上。生产环境我建议直接看真实执行计划:

-- 通过AWR或v$sql拿到sql_id,查真实执行计划和实际行数 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('b7x2k9r1a3m4n', 0, 'ALLSTATS LAST'));

参数说明:三个参数分别是sql_id、child_cursor_number和格式化选项。ALLSTATS LAST会输出实际执行次数、读取行数A-Rows、缓冲块数Buffers。重点比较E-Rows(估算行)和A-Rows(实际行)。两者如果差两个数量级,这个执行计划基本废了,原因大概率是统计信息过期或数据倾斜。

看到执行计划里的TABLE ACCESS FULL先别急着骂全表扫描。小表全表扫描可能比走索引更划算,真正要警惕的是大表上的全表扫描,尤其是db file scattered read等待事件大量出现时。判断标准是:过滤条件能筛掉90%以上数据,且表在千万行级别,还不走索引,那才需要干预。

4.2 索引设计与隐式转换:让SQL能用上索引的常见细节

索引建了,SQL还是不用,最常见的原因就是隐式转换。Oracle的规则是:比较时如果两端类型不一致,它会先把列本身转成目标类型,这就导致列上隐式套了一层函数,索引直接失效。网上常见的“oracle 过滤不可转为数字的字符串”问题就是这么来的。

-- mobile_num是varchar2类型,这里和数字比较,Oracle会隐式转数字 -- 列上套了转换函数后,索引不生效,还会对全表逐行尝试to_number SELECT * FROM user_info WHERE mobile_num = 13800138000; -- 正确的写法:右边也写成字符串 SELECT * FROM user_info WHERE mobile_num = '13800138000';

类似的坑在日期列上更隐蔽。很多人习惯先把日期转成字符串再比较,这是在列上套函数,索引照样失效:

-- 坏写法:create_time列上套了TO_CHAR,无法走索引 SELECT * FROM order_t WHERE TO_CHAR(create_time,'YYYY-MM-DD') = '2025-01-01'; -- 好写法:使用范围条件,让优化器可以走索引 SELECT * FROM order_t WHERE create_time >= DATE'2025-01-01' AND create_time < DATE'2025-01-02';

参数说明:写成范围条件后,列上没有函数,索引范围扫描才可能被选中。DATE'2025-01-01'是SQL标准的日期字面量,比to_date('2025-01-01','yyyy-mm-dd')短且不容易写错。

设计组合索引时,等值条件的列放前面,范围条件放后面,这是基本规则。至于Oracle内置函数,能用的就用,别在SQL里写一堆自造函数,比如过滤字符串就用REGEXP_LIKE、截取就用SUBSTR,这些内置函数的执行计划可控性比自定义函数强太多。存储过程里如果靠字符串拼SQL再EXECUTE IMMEDIATE,每次都要硬解析,能改成绑定变量就绑。

4.3 分页查询与统计信息:OLTP里最容易翻车的两个点

oracle分页的热门程度和翻车率一样高。12c开始提供OFFSET FETCH语法,写起来很舒服,但深分页会把前面的所有行都扫一遍,体验极差:

-- 12c+ 写法,页码越深越慢,OFFSET会扫过前面20万行 SELECT * FROM big_table ORDER BY id OFFSET 200000 ROWS FETCH NEXT 20 ROWS ONLY;

参数说明:OFFSET指定跳过行数,FETCH NEXT 20 ROWS ONLY是取20行。优化器为了拿到这20行,得先把前20万行排好序再丢掉,代价随页码线性增长。对前端分页跳页的场景,我的推荐是限制最大页数或用基于主键的分页方案:

-- keyset分页:记住上一页最后一条记录的id,性能稳定 SELECT * FROM big_table WHERE id > :last_id ORDER BY id FETCH NEXT 20 ROWS ONLY;

说明:last_id是上一页最后一条的id,靠它定位起点,只扫描目标区间,页码多深都不会额外放大开销。适合下拉加载、列表滚动这类场景;如果是那种必须直接跳转到第10000页的报表,建议后端限制最大可跳页数,别硬刚,硬刚一般刚不过这个数据量级别的排序。

统计信息是执行计划的地基。表数据从100万涨到2000万,统计信息没刷新,优化器还用老基数算成本,执行计划自然走偏。手动收集的常用命令:

-- 收集单表统计信息及索引统计,并自动生成直方图 EXEC DBMS_STATS.GATHER_TABLE_STATS( ownname => 'APP', tabname => 'BIG_TABLE', cascade => TRUE, estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => 'FOR ALL COLUMNS SIZE AUTO');

参数说明:cascade => TRUE表示同时收集索引统计信息;estimate_percent用AUTO_SAMPLE_SIZE让Oracle自己决定采样比例,10亿行的大表也能接受;method_opt里的FOR ALL COLUMNS SIZE AUTO会让Oracle对有数据倾斜的列自动建直方图,避免等值条件估算偏差过大。

收集操作有开销,不要在业务高峰对大表硬跑。更稳妥的做法是依赖11g以上的自动统计信息收集任务,并定期检查last_analyzed是否过期。这个检查动作我会每天看一次,放到下文的巡检习惯里。

5. 避坑:性能优化中常见的5个翻车现场

优化做得越多,越发现真正的难题不是不知道技巧,而是踩坑时没法快速回退。下面这五个场景都是我见过或处理过的真实翻车现场,按“现象 → 原因 → 解决”写出,遇到同类问题可以直接照着解。

5.1 现象:改了参数后实例启动失败

现象:执行完ALTER SYSTEM SET sga_target=24G SCOPE=SPFILE后重启,实例起不来,报ORA-27102: out of memory或ORA-00845: MEMORY_TARGET not supported on this system。

原因:SGA目标超过物理内存扣除内核占用后的可用量,或者Linux启用了HugePages但没有分配足够的大页,导致进程无法锁定所需内存。

解决:先备份PFILE,再改参数,给每次调参留后悔药。

-- 修改前先生成PFILE备份 CREATE PFILE='/tmp/pfile_before_opt.ora' FROM SPFILE; -- 若启动失败,用备份PFILE拉起实例 STARTUP PFILE='/tmp/pfile_before_opt.ora'; -- 起来后修正参数,重新生成SPFILE CREATE SPFILE='/u01/app/oracle/product/19c/dbs/spfileorcl.ora' FROM PFILE='/tmp/pfile_before_opt.ora';

提醒一下:HugePages不是单靠sga_target就能配好的,还要同步检查/etc/sysctl.conf里的vm.nr_hugepages和用户memlock上限。

5.2 现象:加了索引反而更慢

现象:某条SQL执行慢,你以为没索引,加完复合索引后,执行时间反而从3秒变成8秒。看执行计划,发现Oracle真的用了新索引,但table access by index rowid的回表次数高得吓人。

原因:索引的选择性太差。比如在status列上建索引,这个列只有3个不同值,其中某个值占了90%的行,优化器根据统计信息估算后仍然选了索引范围扫描,结果每拿到一个rowid就回一次表,物理读比全表扫描还多。

解决:先看列基数,区分度低的列不要建索引。执行计划已经走偏时,可以先用Hint验证回表成本;确认索引确实不合适就直接删掉。核心业务SQL还可以建SQL计划基线(SPM)固定住正确计划,防止优化器在统计信息波动时再换到坏路上。

5.3 现象:AWR报告里全是log file sync

现象:业务高峰期事务量不大,但响应时间突然变长。AWR的Top等待事件里log file sync占比超过40%,CPU利用率却不高,磁盘IO也不饱和。

原因:log file sync是前台会话等待Redo日志写入磁盘的时间。常见诱因是应用每处理一条数据就COMMIT一次,或者日志文件和其他高IO文件放在同一块磁盘上,导致写日志排队。

解决:先改应用端的提交频率,把循环里的单条提交改成批量提交。再确认Redo日志是否放在独立的SSD上,log_buffer是否太小。注意别病急乱投医把log_buffer调到好几G,这个参数过大并不能线性减少log file sync,反而浪费SGA空间。合理的做法是把日志组分布到不同磁盘,并检查ARCHIVELOG归档进程是否拖慢了写日志。

5.4 现象:sqlplus登录oracle数据库出现缓慢

现象:网络没问题,监听lsnrctl status正常,但sqlplus user/pwd@host:1521/orcl要等十几秒才能连上,连上后操作速度正常。

原因:客户端连接时监听器做DNS反解超时,或者listener.log长期不清理导致监听处理变慢。这个问题常被误判成数据库负载高,白白做了一轮性能优化。

解决:在sqlnet.ora里关掉多余的反解和日志增长约速。

# sqlnet.ora SQLNET.INBOUND_CONNECT_TIMEOUT=5 NAMES.DIRECTORY_PATH=(TNSNAMES,EZCONNECT)

说明:SQLNET.INBOUND_CONNECT_TIMEOUT控制连接建立的超时上限,NAMES.DIRECTORY_PATH限定名字解析只走TNSNAMES和EZCONNECT,避免依赖系统DNS。监听日志如果单文件已经到好几个G,停监听、轮转日志后重启监听即可。

5.5 现象:统计信息过期导致执行计划走偏

现象:应用上线时SQL很快,跑了两周后突然变慢。AWR里能看到执行计划里全表扫描,但表上明明有索引。

原因:统计信息没跟上数据变化。比如表从100万行涨到2000万行,自动收集任务没在维护窗口内跑完,优化器还拿旧的行数和数据分布估算成本。

解决:先查统计信息的最后收集时间,确认是否过期。

SELECT table_name, last_analyzed, stale_stats FROM dba_tab_statistics WHERE owner = 'APP' AND table_name = 'ORDER_T';

stale_stats为YES时直接手动收集:

EXEC DBMS_STATS.GATHER_TABLE_STATS('APP','ORDER_T', CASCADE=>TRUE);

如果这个问题反复出现,给核心表固定收集窗口,或对经常被误估的列单独收集直方图。统计信息调优是慢SQL排查里最容易被忽略的一环,很多“SQL昨天还好好的”都是它惹的祸。

6. 把优化做成例行机制:两个能落地的习惯

性能优化不是一次性救火,而是周期性维护。到了这一步,工具、参数、SQL技巧都有了,剩下的是怎么让优化成果不倒退。

第一个习惯是固定月度的AWR基线对比。我每月初会创建一个覆盖上个月全业务的基线模板,用系统级的自动化任务生成,避免手工选快照号。

BEGIN DBMS_WORKLOAD_REPOSITORY.CREATE_BASELINE_TEMPLATE( start_time => TO_TIMESTAMP('2025-02-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS'), end_time => TO_TIMESTAMP('2025-02-28 23:59:59', 'YYYY-MM-DD HH24:MI:SS'), baseline_name => 'BASELINE_FEB_2025' ); END; /

参数说明:start_time和end_time指定基线的起止范围,模板会自动把区间内的快照包成基线,下个月再建一条新基线,两条并排放到 [AWR Baselines] 页签里对比。这个动作做三个月,你就能把业务增长、SQL退化与版本变更关联起来,而不是每次都从零排查。

第二个习惯是每天看一条“Top SQL趋势”查询,替代每天翻AWR。我常用一条SQL从dba_hist_sqlstat里捞近7天平均单次执行最慢的20条SQL。

SELECT s.sql_id, SUM(s.executions_delta) AS exec_count, ROUND(SUM(s.elapsed_time_delta) / 1000000, 2) AS total_sec, ROUND(SUM(s.elapsed_time_delta) / NULLIF(SUM(s.executions_delta), 0) / 1000000, 4) AS avg_sec FROM dba_hist_sqlstat s, dba_hist_snapshot sn WHERE s.snap_id = sn.snap_id AND sn.begin_interval_time > SYSDATE - 7 GROUP BY s.sql_id HAVING SUM(s.executions_delta) > 0 ORDER BY avg_sec DESC FETCH FIRST 20 ROWS ONLY;

逻辑说明:dba_hist_sqlstat存的是每个快照区间内SQL执行统计的增量,executions_delta和elapsed_time_delta是区间内的变化量。用NULLIF防止除零,按avg_sec排序才能筛出“真正慢”的SQL,而不是累计执行时间大但单次很快的SQL。

我自己值班的习惯是:每周一早上先跑这条SQL,再花五分钟过一遍上周的月度基线,新出现的Top SQL或平均耗时突然翻倍的SQL就会暴露在周报里。曾经有一张订单表就是被这条SQL在周一一早抓出来,提前发现了统计信息过期的问题,免得业务投诉之后再去救火。优化工具再多,也比不上一个固定的检查和复盘节奏。希望这篇文章的方法论能帮你在自己的库里少走点弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 1:35:14

Scrapy爬虫实战:从零搭建深度学习论文数据采集管道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 1:34:25

Xenomai 4双内核架构详解:硬实时Linux安装与调优指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 1:34:06

Flink实时推荐系统实战:从用户行为到秒级特征更新

简介&#xff1a;基于Flink的商品实时推荐系统项目包&#xff0c;适合大数据开发与推荐系统学习者&#xff0c;用于解决实时商品热度统计、用户画像构建及个性化推荐排序等核心问题。资源共109个文件、压缩包3.74MB&#xff0c;以68个Java源文件为主&#xff0c;另有SQL建表脚本…

作者头像 李华
网站建设 2026/10/3 1:33:47

教务管理系统课程设计:E-R图、建表SQL与ASP增删改查全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 1:33:27

ESP32接大模型不算AI硬件:8个工程化难题与端云协同方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 1:32:17

2.4G跳频算法与nRF52832切信道实现:抗干扰实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华