oracle包含的2种代价模型:
oracle通过隐含参数“_optimizer_cost_model"控制使用哪种代价模型
1io代价模型
cost=iocost
2cpu代价模型
cost=iocost+cpucost
在代价转换的过程中,所有的代价都转换为单数据块读的单次代价
存在四种类型的io,单块读,多块读,直接数据块读和直接数据块写
iocost=上述四种io的消耗
选择率与密度,空值,唯一值,以及柱状图数据有关。过滤条件的选择率
a and b a的选择率*b的选择率
a or b a的选择率+b的选择率-a*b
not a 1-a的选择率
在未引入系统统计信息之前,oracle会假设下面的2中情况是成立的:
1 单块读的平均消耗时间和多块读的平均消耗时间相等
2全表扫的时候,根据下面的情况计算全表扫描的成本
mbdivisor=1.675*power(db_file_multiblock_read_count,0.6581)
tsc=blocks/mbdivisor
10.2中的mbdivisor大约是4.4,11.2中的改值大约是3.6
cpu cost model在开启的情况下,全表扫描的计算公式如下:
tsc cost=i/o cost+cpu cost
i/o cost =1+ceil(blocks/mbrc)*(mreadtim/sreadtim)
cpu cost=round(#cpucycles/cpuspeed/1000/sreadtim)
系统的统计信息放在了aux_stats$中
查看统计信息收集的历史
wri$_optstat_tab_history
执行计划中的记录数是数据记录数*选择率,运行时间的估算,总的单数据块读的io代价*单次数据块读时间
全表扫描的大概估计:
索引范围扫描:分支节点读取的数据块数和叶子节点数据块数决定。分支节点数据块的读取数等于索引数的分支节点层数。叶子节点的计算是叶子节点数*条件选择率
所以索引的代价大概是层数+叶结点数*选择率*optimizer_index_cost_adj
索引rowid访问表的计算:这边涉及到一个聚簇因子,它是代表了数据块的聚集性。
iocost=索引树高度+叶子数据块数量*access的选择率+聚簇因子*filter的选择率*optica/100
索引范围扫描的计算公式:
i/o cost= index access i/o cost + table access i/o cost
index access i/o cost= blevel+ceil(#leaf_block*ix_sel)
table acess i/o cost=ceil(clustering_factor*ix_sel_with_filters)
列上没有直方图,且没有null值得情况下,列的选择率=(1/num_distinct)
列上没有直方图,且有null值的情况下,列的选择率=(1/num_distinct)*((num_rows-num_nulls)/num_rows)
列上的low_value和high_value是用来评估列做范围查询时的可选择率
目标列大于指定值val,且val处于low_value与high_value之间
selectivity=((high_value-val)/(high_value-low_value)*null_adjust;
null_adjust=(num_rows-num_nulls)/num_rows
目标列小于指定val,且val处于low和high之间
selectivity=(val-low_value)/(high_value-low_value)*null_adjust;
null_adjust=(num_rows-num_nulls)/num_rows
目标列大于或等于指定值val,且val处于low和high之间
selectivity=((high-val)/(high-low)+1/num_distinct)*null_adjust
目标列小于或等于指定值val,且val处于low和high之间
selectivity=((val-low)/high-low)+1/num_distinct)*null_adjust
目标列在指定值val1和val2之间,且val1和val2都在low和high之间
selectivity=((val2-val1)/high-low)+2/num_distinct)*null_adjust
对于使用了绑定变量的sql,启动绑定变量窥探,则对于>的范围查询
Selectivity = (high_value - limit) / (high_value - low_value) limit是绑定变量的值
Cardinality = rows*selectivity
对于使用了绑定变量的sql,如果没有启用绑定变量窥探,则对于>的范围查询
selectivity=0.05
没有启用绑定变量窥探,对于between and的查询
selectivity=0.0025
--测试选择率
SQL> select count(*) from tt where object_id is null;
COUNT(*)
----------
99
SQL> select count(*) from tt;
COUNT(*)
----------
49859
exec dbms_stats.gather_table_stats(ownname => 'baixyu',tabname => 'TT',method_opt => 'for all columns size 1');
SQL> select count(*) from tt where object_id is null;
COUNT(*)
----------
99
SQL> select count(*) from tt;
COUNT(*)
----------
49859
select num_distinct,low_value,high_value,num_nulls from user_tab_col_statistics s where s.table_name='TT' and s.column_name='OBJECT_ID';
49760 C2020BC3061C2299
SQL> select * from tt where object_id=120;
执行计划
----------------------------------------------------------
Plan hash value: 264906180
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 93 | 159 (2)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| TT | 1 | 93 | 159 (2)| 00:00:02 |
--------------------------------------------------------------------------
SQL> select 1/49760*((49859-99)/49859)*49859 from dual;
1/49760*((49859-99)/49859)*49859
--------------------------------
1
SQL> select * from tt where object_id>120 and object_id<200;
已选择78行。
执行计划
----------------------------------------------------------
Plan hash value: 264906180
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 76 | 7068 | 159 (2)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| TT | 76 | 7068 | 159 (2)| 00:00:02 |
--------------------------------------------------------------------------
select (((200-120)/(52744-101)+2/49760)*49760/49859)*49859 from dual;
(((200-120)/(52744-101)+2/49760)*49760/49859)*49859
---------------------------------------------------
77.6187907
在列上没有直方图的情况下,统计信息中的density可以认为是1/num_distinct,在列上有等频直方图时,density的计算公式为
density=1/(2*num_rows*null_adjust)
null_adjust-(num_rows-num_nulls)/num_rows
如果查询条件的输入值等于目标列的某个bucket的endpoint_value那么 列的cardinality的计算公式是
cardinality=num_rows*selectivity
selectivity=bucketsize/num_rows
bucketsize=current_endpoint_number-previous_endpoint_number
如果查询条件的输入值不等于目标列的任意一个bucket的endpoint_value,计算公式如下
cardinality=num_rows*selectivity
selectivity=min(bucketsize)/(2*num_rows)
bucketsize=current_endpoint_number-previous_endpoint_number;
---怎么不直接设置成0?
当列上的直方图信息是等高直方图的时候,等值查询的估算是
1如果查询条件输入值是popular value
cardinality=num_rows*selectivity
selectivity=(buckets_this_popular_value/buckets_totoal)*null_adjust
null_adjust=(num_rows-num_nulls)/num_rows
2如果是unpopular value
分成三种情况。。。。