drop table test purge;
create table test as select * from dba_objects;
--蒐集統計信息,不蒐集直方圖信息
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname =>'SCOTT',
tabname => 'TEST',
estimate_percent => 100,
method_opt => 'for all columns size 1',
degree =>DBMS_STATS.AUTO_DEGREE,
cascade=>TRUE
);
END;
/
select owner,blocks from dba_tables where owner='SCOTT' and table_name='TEST';
注意:如果沒有收集過系統統計信息,那麼Oracle採用非工作量統計,如果收集了,Oracle採用工作量統計的計算方法
Cost= (
#SRds * sreadtim+ ---SRds=0 (全表掃描沒有單塊讀)
#MRds * mreadtim+ ---MRds=BLOCKS/MBCR=1105/128, mreadtim=266
CPUCycles / cpuspeed /1000 ---CPUCycles=PLAN_TABLE.CPU_COST,cpuspeed=2500
) / 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)
mreadtim=ioseektim+db_file_multiblock_count*db_block_size/iotftspeed
sreadtim=ioseektim+db_block_size/iotfrspeed
CPUCycles 等於PLAN_TABLE裏面的CPU_COST—這個ORACLE未解密,無法知道怎麼計算的
Cost= (
#SRds * sreadtim+ ---SRds=0
#MRds * mreadtim+ ---MRds=BLOCKS/MBCR=10003/12, mreadtim=30
CPUCycles / cpuspeed /1000 ---CPUCycles=PLAN_TABLE.CPU_COST,cpuspeed=19215491
) / sreadtime
我這裏因爲MBRC 爲0,所以CBO採用了非工作量(noworkload)來計算成本
#SRds=0,因爲是全表掃描,單塊讀爲0
#MRds=表的塊數/多塊讀參數=1105/128
mreadtim=ioseektim+db_file_multiblock_count*db_block_size/iotftspeed
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;
sreadtim=ioseektim+db_block_size/iotfrspeed
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"
fromdual;
CPUCycles 等於 PLAN_TABLE裏面的CPU_COST
explain plan for select count(*) from test;
select cpu_cost from plan_table;
cpuspeed 等於 CPUSPEEDNW= 1194
那麼整個COST公式代入值計算爲:
select ceil((1105/128*266+19215491/1194/1000)/12) from dual;
select count(*) from test;
手工計算出來的COST用四捨五入等於193,和我們看到的194有差別,這是由於隱含參數_tablescan_cost_plus_one參數造成的
Set pagesize 500 linesize 500
Col name for a30
Col value for a30
Col describ for a60
SELECT x.ksppinm NAME, y.ksppstvl VALUE,x.ksppdesc describ
FROMx$ksppi x, x$ksppcv y
WHERE x.inst_id = USERENV ('Instance')
AND y.inst_id = USERENV ('Instance')
AND x.indx = y.indx
AND x.ksppinm LIKE '%_table_scan_cost_plus_one%';
根據該參數的描述,在table full scan和index fast full scan的時候會將cost+1
那麼我把改參數禁止了試一試
這次得到的Cost等於193,與計算值正好匹配,現在更改db_file_multiblock_read_count參數
select ceil(1105/16*42/12+19215491/1194/12/1000) from dual;
和實際中執行計劃的COST值一致
總結:在非工作量模式下,db_file_multiblock_read_count會影響全表掃描表的COST值,在11gR2中,全表掃描計算Cost的方式依然和9i/10g一樣,沒有變化。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname =>'TEST',
tabname => 'TEST',
estimate_percent => 100,
method_opt => 'for all columns size1',
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 32910
SQL> select count(distinct dbms_rowid.rowid_block_number(rowid)) from test;
COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
23563
SQL> show parameter multi
NAME TYPE VALUE
----------------------------------------------- ------------------------------
db_file_multiblock_read_count integer 16
parallel_adaptive_multi_user boolean TRUE
SQL> explain plan for select * from test where owner='SYS';
已解釋。
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-----------------------
Plan hash value: 1357081020
--------------------------------------------------------------------------
| Id | Operation | Name |Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0| SELECT STATEMENT | | 77444 | 7336K| 7223 (1)| 00:01:27 |
|* 1| TABLE ACCESS FULL| TEST | 77444 | 7336K| 7223 (1)| 00:01:27 |
--------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
1- filter("OWNER"='SYS')
已選擇13行
COST=7223
select count(*) from test where owner='SYS';
--986880
全表掃描成本計算公式:
成本的計算方式如下:
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 =32910/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 fromsys.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 * from test where owner='SYS';
已解釋。
SQL> select cpu_cost fromplan_table;
CPU_COST
----------
720716510
720716510
cpuspeed 等於 CPUSPEEDNW=2696.05568
SQL> select pname, pval1 fromsys.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 blockreads 單塊讀次數) * 12
#MRds * mreadtim =(number of multi blockreads) * 42=32910/16 * 42=86388.75
sreadtime=12
SQL> select ceil(32910/16*42/12 + 720716510/2696.05568/1000/12) from dual;
CEIL(32910/16*42/12+720716510/2696.05568/1000/12)
-------------------------------------------------
7222
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次數