SQL> select * from v$version where rownum=1; BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production 创建手动管理的表空间,blockssize 8k create tablespace test datafile '/oradata/june/test.dbf' size 50m autoextend on maxsize 200m uniform size 1m segment space management manual blocksize 8k; 创建测试用户test,默认表空间 test SQL> create user test identified by oracle default tablespace test; create table test as select * from dba_objects where 1=0 ; alter table test pctfree 99 pctused 1; pctfree 保留空间 用于update SQL> insert into test select * from dba_objects where rownum<2; 1 row created. SQL> commit; Commit complete. alter table test minimize records_per_block; SQL> insert into test select * from dba_objects where rownum<1000; 999 rows created. SQL> commit; Commit complete. 现在使用了1000个块 BEGIN DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST', tabname => 'TEST', estimate_percent => 100, method_opt => 'for all columns size 1', degree => DBMS_STATS.AUTO_DEGREE, cascade=>TRUE ); END; SQL> select owner,blocks from dba_tables where owner='TEST' and table_name='TEST'; OWNER BLOCKS ------------------------------ ---------- TEST 1000 SQL> select count(distinct dbms_rowid.rowid_block_number(rowid)) from test; COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)) --------------------------------------------------- 1000 SQL> alter system set db_file_multiblock_read_count=16; System altered. SQL> select count(*) from test; Execution Plan ---------------------------------------------------------- Plan hash value: 1950795681 ------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 220 (0)| 00:00:03 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | TABLE ACCESS FULL| TEST | 1000 | 220 (0)| 00:00:03 | ------------------------------------------------------------------- Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 1003 consistent gets 79 physical reads 0 redo size 422 bytes sent via SQL*Net to client 420 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed COST=220 全表扫描成本计算公式: 成本的计算方式如下: Cost = ( #SRds * sreadtim + #MRds * mreadtim + CPUCycles / cpuspeed ) / sreadtime #SRds - number of single block reads 单块读次数 #MRds - number of multi block reads 多块读次数 #CPUCyles - number of CPU cycles CPU时钟周期数 sreadtim - single block read time 一次单块读耗时(单位milliseconds 毫秒,1000毫秒等于1秒) mreadtim - multi block read time 一次多块读耗时(单位milliseconds 毫秒,1000毫秒等于1秒) cpuspeed - CPU cycles per second CPU频率(单位MHZ)--每秒钟CPU做多少个轮训 全表扫描多块读 那么#SRds=0 #MRds=16,每次I/O读16个块 mreadtim =1000/16 SQL> select pname, pval1 from sys.aux_stats$ where sname='SYSSTATS_MAIN'; PNAME PVAL1 ------------------------------ ---------- CPUSPEED CPUSPEEDNW 2696.05568 IOSEEKTIM 10 IOTFRSPEED 4096 MAXTHR MBRC MREADTIM SLAVETHR SREADTIM 9 rows selected. 这里因为MBRC 为0,所以CBO采用了非工作量(noworkload)来计算成本,所有的系统全是 用的 非工作量。 计算mreadtim - multi block read time 一次多块读耗时(单位milliseconds 毫秒,1000毫秒等于1秒) mreadtim=ioseektim+db_file_multiblock_count*db_block_size/iotftspeed ioseektim:寻道寻址的时间 db_file_multiblock_count*db_block_size=16*8K=128K ---一次I/O的数据量 db_file_multiblock_count*db_block_size/iotftspeed=多块读耗时时间 iotftspeed:I/O传输速度 mreadtim(多块读耗时)=寻道寻址的时间+多块读耗时 SQL> select (select pval1 from sys.aux_stats$ where pname = 'IOSEEKTIM') + (select value from v$parameter where name = 'db_file_multiblock_read_count') * (select value from v$parameter where name = 'db_block_size') / (select pval1 from sys.aux_stats$ where pname = 'IOTFRSPEED') "mreadtim" from dual; 2 3 4 5 6 7 mreadtim ---------- 42 多块读的耗时有42毫秒 sreadtim(单块读耗时)=ioseektim+db_block_size/iotfrspeed SQL> select (select pval1 from sys.aux_stats$ where pname = 'IOSEEKTIM') + (select value from v$parameter where name = 'db_block_size') / (select pval1 from sys.aux_stats$ where pname = 'IOTFRSPEED') "sreadtim" from dual; 2 3 4 sreadtim ---------- 12 单块读耗时12毫秒 操作系统CPU 和磁盘信息Oracle都可以获取到 CPUCycles 等于 PLAN_TABLE里面的CPU_COST---这个ORACLE未解密,无法知道怎么计算的 SQL> explain plan for select count(*) from test; Explained. SQL> select cpu_cost from plan_table; CPU_COST ---------- 7271440 cpuspeed 等于 CPUSPEEDNW= 2696.05568 SQL> select pname, pval1 from sys.aux_stats$ where sname='SYSSTATS_MAIN'; PNAME PVAL1 ------------------------------ ---------- CPUSPEED CPUSPEEDNW 2696.05568 IOSEEKTIM 10 IOTFRSPEED 4096 MAXTHR MBRC MREADTIM SLAVETHR SREADTIM 9 rows selected. 成本的计算方式如下: Cost = ( #SRds * sreadtim + #MRds * mreadtim + CPUCycles / cpuspeed ) / sreadtime #SRds * sreadtim =0 单块读次数为0 #SRds * sreadtim=(number of single block reads 单块读次数) * 12 #MRds * mreadtim =(number of multi block reads) * 42=1000/16 * 42=2625 CPUCycles / cpuspeed=7271440/2696.05568=2697/1000=2.697 ---每秒转换为每毫秒/1000 sreadtime=12 那么COST=1000/16*42/12+7271440/2696.05568/12/1000 SQL> select ceil(1000/16*42/12+7271440/2696.05568/12/1000) from dual; CEIL(1000/16*42/12+7271440/2696.05568/12/1000) ---------------------------------------------- 219 Cost = ( #MRds * mreadtim + CPUCycles / cpuspeed ) / sreadtime Cost = #MRds +( CPUCycles / cpuspeed ) / sreadtime 最终的成本计算公式=Cost = #MRds (忽略CPU的情况下,就是多块读的次数=1000/16) 说明减少 物理 io扫描次数,是SQL优化的核心思想, Cost = #MRds * mreadtim/ sreadtime #MRds - number of multi block reads 多块读I/O次数