3 直方图--优化主题系列

来源:互联网 发布:淘宝联盟淘宝身份认证 编辑:程序博客网 时间:2024/06/05 07:47

直方图当某列数据分布不均衡,为了让CBO能生成最佳的执行计划,我们可能需要对表收集直方图,直方图最大的桶数(Bucket)254。收集直方图是一个很耗时的过程,如无必要,千万别去收集直方图。

 

请注意在 OLTP 系统中

如果没有必要

千万不要去收集直方图统计

 

因为你收集了直方图

可能遇到绑定变量窥探

9i,10g里面几乎没有好办法解决

11g里出了一个自适应游标解决了这个但也有风险

之所以有绑定变量窥探

就是因为收集了直方图

 

我们来举个例子探究下直方图到底是干什么的

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 sizeskewonly',

                               no_invalidate    => FALSE,

                               degree           => 1,

                               cascade          => TRUE);

END;

/

select a.column_name,

       b.num_rows,

       a.num_distinct Cardinality,

       round(a.num_distinct /b.num_rows * 100, 2) selectivity,

       a.histogram,

       a.num_buckets

  from dba_tab_col_statistics a, dba_tables b

 where a.owner = b.owner

   and a.table_name = b.table_name

   and a.owner = 'SCOTT'

   and a.table_name = 'TEST';

为了讲解直方图,我收集统计信息的时候是method_opt => 'for all columns size skewonly',

正式的生产环境中,最好别用allcolumns方式收集直方图,因为allcolumns几乎会对所有列都收集直方图信息,但是有些列并不会出现在where条件中,我们去收集并不会出现在where条件中的列就浪费了资源,并且OLTP环境中,能不收集直方图就不要收集直方图。

Oracle的直方图有两种:

一种是频率直方图(FREQUENCYHISTOGRAM),当列中Distinct_keys较少(小于254),如果不手工指定直方图桶数(BUCKET)Oracle就会自动的创建频率直方图,并且桶数(BUCKET)等于Distinct_Keys

 

owner这列基数等于多少?发现这个规律没?

当一个列的基数小于254

那么直方图的桶数就等于列的基数

这种直方图就叫频率直方图

 

另外一种是高度平衡直方图(HEIGHTBALANCED),当列中Distinct_keys大于254,如果不手工指定直方图桶数(BUCKET)Oracle就会自动的创建高度平衡直方图。

 

下面来看下直方图到底有何作用,基于刚才创建的表test创建一个索引idxowner

create index idx on test(owner);

select ,count(*)from table group byorder by 2 desc;


大家觉得走全表扫描是对的还是错的?

select 31148/72834 from dual;


返回了42%的数据因此走全表扫描、不该走索引是对的

 

select * from test where owner='SCOTT'; (执行计划截图略)

这个执行计划是对的还是错的

之前对test表收集了直方图 

 

在对列owner进行检查的时候

oracle会根据列的分布

CBO很聪明会自动的选择走索引或者不走索引

 

现在删除直方图统计,看看CBO是否还能做出正确的选择

BEGIN

 DBMS_STATS.GATHER_TABLE_STATS(ownname         => 'SCOTT',

                               tabname          => 'TEST',

                               estimate_percent => 100,

                               method_opt       => 'for all columns size 1',

                               no_invalidate    => FALSE,

                               degree           => 1,

                               cascade          => TRUE);

END;

/

method_opt=> 'for all columns size 1' 表示所有列都不收集直方图

select a.column_name,

       b.num_rows,

       a.num_distinct Cardinality,

       round(a.num_distinct /b.num_rows * 100, 2) selectivity,

       a.histogram,

       a.num_buckets

  from dba_tab_col_statistics a, dba_tables b

 where a.owner = b.owner

   and a.table_name = b.table_name

   and a.owner = 'SCOTT'

   and a.table_name = 'TEST';


没有直方图了来看看刚才的两个查询



是不是走索引逻辑读反而大了很多??

说明查询SYS走索引不合理

为什么查询SCOTT走索引查询SYS也走索引呢

看下执行计划ROWS这列 CBO认为应返回2428条记录

而总条数为72834OWNER列的基数是30


从七万多条数据查出2428  走索引还是全表扫描?


查询SYS时的执行计划ROWS列也是2428??

实际查询返回行31148 CBO仍然认为返回2428

返回了42%的数据

也就是说CBO认为无论查询哪个条件都返回2428

没收集直方图 CBO认为这些列的值是平均分布的

 

总结:直方图是告诉CBO每个列的值的分布情况




如上收集完此列直方图后

CBO估算出来的和返回的真实行数一致CBO估算更为准确

 

频率直方图基数小于254最为精准

因为它能完全的体现出这个值对应多少条几记录

 

高度平衡直方图则不然

因为最大的桶数只有254某列基数超过254

这个时候记录某个值有多少记录就会共享记录

就是某些数据共享记录在同一个桶里因此并非精准

 

统计一下表的SEGMENT_SIZE或者你来统计一下表一共有多少个块

select sum(blocks) from dba_segments where owner='SCOTT' andsegment_name='TEST';--1152

多块读的个数是不是16???那么一共要消耗的I/O是多少?

select 1152/16 from dual; --72 是不是全表扫描只需要 72 IO 就搞定了?

我们来计算索引扫描的I/O 次数 

SQL优化最最核心的思想 就是减少 I/O 扫描次数

索引扫描一般的是不是单块读?除了 INDEXFAST FULL SCAN

INDEX RANGE SCAN 是不是单块读?

刚才是不是要返回 2W多行数据

假设索引的高度是2那么你扫描索引最少都要扫描 2个块是不是耗费了 2I/O?

刚才是不是索引返回2W多行数据

我们假设索引一个索引块能存100个记录

2W 条记录返回是不是200个块

扫描 200个块是单块读现在是不是要进行 200多次 I/O 扫描次数?

索引里面是不是要存储 ROWID

2w 多行数据2w多个 ROWID是不是也要消耗几百个 I/O?

是不是扫描索引要耗费几百次的 I/O???走全表扫描只需要72

假设我有 1000W行的表返回 500W

假设 1000W 行的表有多大?你们觉得1000W行的表的SEGMENT_SIZE有多大?

5W 6.5MB乘以200ok了? 1300 MB

我们来算一下 1300 MB 走全表扫描要消耗多少次I/O??? 8k多块读参数16

1W 多次I/O对吧 selectceil(1300*1024/8/16) from dual; --10400

我们来算索引扫描要多少次I/O?

1000w返回 500W索引的高度3一个索引块100条记录

是不是扫描索引块要扫描 5W次?有500WROWID要回表回表也是单块读啊也要几万个I/O

是不是接近 10W I/O???走全表扫描是不是 1WI/O

等待事件是什么?db file sequential read?

你们去监控一个SQLN久不出结果是不是有可能本来该全表扫描的但是走了索引扫描??

AWR db file sequential read 排名第一应该咋搞???是不是想到SQL优化了??

我问一下一次 I/O多块读与一次I/O单块读时间差别大不大?

现代的存储多块读其实 是多个存储读的存储底层做了条带化

一次多块读多个磁盘同时的读一次单块读同一个磁盘速度差别不是太大

以后我们就认为 多块读与单块读速度一样 

所以 SQL 优化就只关心扫描次数了多块读与单块读 具体的时间差别体现在搬运时间磁盘寻道寻址

这些时间都是 0.00计算的只有 I/O扫描次数上了几千万次

SQL优化核心思想就是减少I/O扫描次数

直方图搞明白是干嘛的了没?如果一个列不去收集直方图

那么基数算法一样的块读是不是性能要高??1时间I/O次数



原创粉丝点击