最近接手了一个客户系统的性能优化案例,客户反映Oracle数据库查询响应缓慢,严重影响业务操作体验。作为有十年Oracle调优经验的DBA,我首先需要全面了解系统现状。
这个系统运行在32核服务器上,存储采用中端SAN阵列。从AWR报告来看,AAS(Average Active Sessions)值为0.15(计算方式:AAS=dbtime/elapsed/cpus=294.59/60/32=0.15),说明数据库整体负载其实很小。但用户确实感知到明显的性能下降,特别是在执行某些特定查询时。
通过进一步分析,发现几个关键现象:
从等待事件统计来看,最突出的两类等待是:
具体到等待事件明细:
db file scattered read:多块读操作,通常与全表扫描相关gc cr block busy:全局缓存争用虽然平均等待时间看起来不大(毫秒级),但累积效应会导致用户体验明显变差。这里有个重要发现:I/O等待可以通过SQL优化来降低,而gc等待可能需要考虑应用分离。
内存相关指标显示:
通过检查内存顾问(Buffer Pool Advisory和Shared Pool Advisory),确认内存分配本身是足够的。这说明命中率低的原因是:
从SQL统计信息中筛选出物理读最高的4条SQL:
值得注意的是,第1和第4条SQL都未能执行成功,但它们消耗了大量I/O资源。
以SQL_ID byfu51bw4prwv为例,其执行计划显示:
sql复制select count(*) as COLUMN1
from umt_bil1.tg_cdr04
where long_type1='3' and partition_id=21
执行计划关键问题:
这种全分区扫描正是导致高物理读的罪魁祸首。更合理的做法应该是直接访问特定分区。
针对发现的SQL问题,给出具体优化方案:
sql复制-- 原SQL
select count(*) from umt_bil1.tg_cdr04
where long_type1='3' and partition_id=21;
-- 优化后
select count(*) from umt_bil1.tg_cdr04 PARTITION(P21)
where long_type1='3';
sql复制-- 在long_type1和partition_id上创建联合索引
CREATE INDEX idx_tg_cdr04_type_part ON tg_cdr04(long_type1, partition_id)
LOCAL TABLESPACE indx;
sql复制-- 增加shared_pool保留区
ALTER SYSTEM SET shared_pool_reserved_size=500M SCOPE=BOTH;
-- 调整cursor_sharing
ALTER SYSTEM SET cursor_sharing=FORCE SCOPE=SPFILE;
sql复制-- 对关键表收集统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS('UMT_BIL1','TG_CDR04',
method_opt=>'FOR ALL COLUMNS SIZE AUTO',
degree=>8);
实施上述优化后,性能指标明显改善:
通过这个案例,总结几点重要经验:
sql复制-- 不好的做法
v_sql := 'SELECT...'||variable;
DBMS_SQL.PARSE(v_cursor, v_sql);
-- 推荐做法
v_sql := 'SELECT...WHERE col=:1';
DBMS_SQL.PARSE(v_cursor, v_sql);
DBMS_SQL.BIND_VARIABLE(v_cursor, ':1', value);
这个案例再次验证了Oracle性能问题的80/20法则——80%的性能问题往往源于20%的糟糕SQL。作为DBA,我们需要建立系统的SQL监控和优化机制,而不是等问题发生后再救火。