早期Oracle查询优化器的开销计算是基于执行SQL语句所需要的物理读,这个方法被叫做I/O开销模式(I/O cost model),这个方法的主要缺点是认为单块读和多块读开销相当。在Oracle 8i中,初始化参数optimizer_index_caching和optimizer_index_cost_adj解决了这个问题,但参数的默认值仅适用于报告系统和数据仓库。直到Oracle 9i,ORacle引入了一种新的CPU开销模式(CPU cost model),消除了这个缺陷,但必须提供数据库引擎所在的系统的性能信息,即系统统计信息。这篇文章将介绍系统统计信息和收集系统统计信息的方法。
系统统计信息放在表aux_stats$中,可以通过下面的方法查看该视图中的数据(需要DBA用户或者有该表的查看权限):
select * from aux_stats$;
SNAME PNAME PVAL1 PVAL2
---------------------------------------------------------------------
SYSSTATS_INFO STATUS COMPLETED
SYSSTATS_INFO DSTART 08-30-2005 15:04
SYSSTATS_INFO DSTOP 08-30-2005 15:04
SYSSTATS_INFO FLAGS 1
SYSSTATS_MAIN CPUSPEEDNW 484.974958263773
SYSSTATS_MAIN IOSEEKTIM 10
SYSSTATS_MAIN IOTFRSPEED 4096
SYSSTATS_MAIN SREADTIM
SYSSTATS_MAIN MREADTIM
SYSSTATS_MAIN CPUSPEED
SYSSTATS_MAIN MBRC
SYSSTATS_MAIN MAXTHR
SYSSTATS_MAIN SLAVETHR
表中的数据包括3个部分,使用SNAME区分:
1)SYSSTATS_INFO表示系统统计信息状态和时间
STATUS在正确收集时为COMPLETED;如果出现问题,显示为BADSTATS,在这种情况下查询优化器将不使用统计信息;该字段还可以为:MANUALGATHERING和AUTOGATHERING。
2)SYSSTATS_MAIN表示系统统计信息结果集
该部分的信息分为非工作量统计信息(noworkload statistics)和工作量统计信息(workload statistics),两者的主要差别是度量I/O子系统性能的方法有所不同,后面具体讨论。
非工作量统计在Oracle 10g之后总是提供,包括:
CPUSPEEDNW 一个CPU一秒钟能处理的操作数,单位百万次/秒
IOSEEKTIM 平均磁盘寻道时间,单位毫秒,默认10
IOTFRSPEED 平均每毫秒从磁盘传输的字节数,默认为4096
工作量统计信息必须显示地收集后才可用,包括:
SREADTIM 单块数据平均读取时间,单位毫秒
MREADTIM 多块数据平均读取时间,单位毫秒
CPUSPEED 一个CPU一秒钟能处理的操作数,单位百万次/秒
MBRC 一次多块读操作的平均读取数据块数
MAXTHR 整个系统的最大I/O吞吐量(字节/秒)
SLAVETHR 并行处理中从属线程的平均I/O吞吐量(字节/秒)
3)SYSSTATS_TEMP用来计算系统统计信息,只有收集工作量统计信息时才可用
使用工具包dbms_stats中的存储过程gather_system_stats收集系统统计信息,可以收集非工作量统计信息或者工作量统计信息。
从Oracle 10g开始,总是提供默认的非工作量统计信息,如果显式地删除,下次数据库启动时会自动收集。收集非工作量统计信息要在空闲的系统上进行,使用那个下面的语句:
begin
dbms_stats.gather_system_stats(gathering_mode => 'noworkload');
end;
收集非工作量统计信息只需要很短的时间,但有时需要重复收集多次才能生效。
收集工作量统计信息,不能使用空闲的系统,以为数据库引擎要利用正常的数据库负载来评估I/O子系统的性能。
工作量统计信息的收集包含下面的步骤:
1)执行快照并存储各种性能指标的初始值到数据字典表aux_stats$(列SNAME设定为SYSSTATS_TEMP)。使用下面的方式来启动:
begin
dbms_stats.gather_system_stats(gathering_mode => 'start');
end;
2)等待足够的时间产生一个有代表性的负载时候再进行另一次快照,等待时间通常不少于30分钟;
3)进行第二次快照,使用下面的语句:
begin
dbms_stats.gather_system_stats(gathering_mode => 'stop');
end;
4)Oracle根据两次快照之间的差值,计算出系统统计信息,如果无法计算的话,将被设置为空。
你也可以指定gathering_mode为interval,并指定间隔时间来收集工作量统计时间,这样就不用手工停止快照,下面的命令指定30分钟后进行第二次快照:
begin
dbms_stats.gather_system_stats(gathering_mode => 'interval',
interval => 30);
end;
需要注意上面的语句并不会延续30分钟,它只是启动一个作业调度后返回。
使用工作量统计信息需要尽量避免收集到没有代表性的工作量信息,可以收集多天的工作量统计信息,然后将mbrc、mreadtim、sreadtim和cpuspeed指标取平均值,maxthr和slavethr取最大值,再使用delete_system_stats过程删除旧的系统统计信息,然后使用set_system_stats设定新的系统统计信息,存储过程如下:
begin
dbms_stats.delete_system_stats();
dbms_stats.set_system_stats(pname => 'CPUSPEED', pvalue => 772);
dbms_stats.set_system_stats(pname => 'SREADTIM', pvalue => 5.5);
dbms_stats.set_system_stats(pname => 'MREADTIM', pvalue => 19.4);
dbms_stats.set_system_stats(pname => 'MBRC', pvalue => 53);
dbms_stats.set_system_stats(pname => 'MAXTHR', pvalue => 1136136192);
dbms_stats.set_system_stats(pname => 'SLAVETHR', pvalue => 16870400);
end;