数据分布不均衡会导致在查询该列的时候, 要么走全表扫描, 要么走索引扫描, 这个时候很容易 走错执行计划。说白了就是判断列适不适合走索引 ( 根据 where 条件后跟的值不同,返回结果也不同,那么该列走不走索引的结果也不同 ) 。
基数高的列,一般数据分布一定均衡,比如主键列
1 a
2 b
3 c
。。。。。。因为他没有重复的,优化器认为他每一个条件过滤出来的都只占一行,所以在执行计划里就会预估过滤或者返回出一行,但是如果分布不均匀的数据,我某个条件过滤出占总行数 50% 以上的数据,由于没收集直方图,导致执行计划预估为 1 行,那么执行计划就肯定走错了。!
如果 没有对基数低的列收集直方图统计信息 , 基于成本的优化器( CBO )会认为该列数据分布是均衡的 ( 就会默认去走索引,这时候如果返回的数据很多,那么明显就走了错误的执行计划 ) 。
以测试表 test 为例, 用实验讲解直方图。
首先我们对测试表 test 收集统计信息,在收集统计信息的时候, 不收集列的直方图, 语句 for all columns size 1 表示对所有列都不收集直方图。
BEGIN DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST', tabname => 'TEST', estimate_percent => 100, method_opt => 'for all columns size 1', no_invalidate => FALSE, degree => 1, cascade => TRUE); END; /
Histogram 为 none 表示没有收集直方图。
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 = 'TEST' and a.table_name = 'TEST' ;
owner 列基数很低, 现在我们对 owner 列进行查询。
set autot trace set autotrace off(关闭用这个) select * from test where owner= 'ORACLE_OCM';
查询 owner='ORACLE_OCM' 返回了 6 条数据 , 但是 CBO 在计算 Rows 的时候认为 owner='TEST' 返回 1766 条数据, Rows 估算得不是特别准确。从 22955 条数据里面查询出 6 条数据 , 应该走索引 , 所以现在我们对 owner 列创建索引。
create index idx_owner on test(owner);
然后查询owner='SYS'用户
select * from test where owner= 'SYS';
发现还是走的索引,从 2W 多行数据中返回 14567 条数据,明显是不应该走索引的,因此 走错了执行计划
因为 owner 的基数太低了,只有 13 ,而返回的总行数有 22955 ,很巧妙的发现 22955/13=1766 ,即 CBO 估算的 ROWS ,这是因为我们在收集统计信息的时候
没有(或者说没对 owner 这列)进行直方图收集。下面收集一下。
BEGIN DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST', tabname => 'TEST', estimate_percent => 100, method_opt => 'for columns owner size skewonly', no_invalidate => FALSE, degree => 1, cascade => TRUE); END; /
查看 owner 列直方图收集情况
然后在查询一下 SQL ,看看是否会走错执行计划
最后发现终于走对了, ROWS 也变得相应正确了。
收集直方图究竟完成了什么操作呢?对 owner 列收集直方图其实就相当于运行了以下 SQL.
select owner,count(*) from test group by owner;
其中 NUM_BUCKETS,也就是 直方图桶的作用就是记录收集 count(*),也就是提前收集该列的数据分布情况,让CBO优化提前知道有几行,也就是为什么条件后跟的值不同,返回的rows不同的原因了,这样 就可以选择正确的执行计划了。
根据基数分为两种直方图:
1 、 num_buckets 超过 254 的为等高直方图,即—— HEIGHT BALANCED
2 、 num_buckets 小于 254 的为频率直方图,即—— FREQUENCY
11g :
基数 超过 254 的,直方图桶数最高也就是 254 ,一个桶会放多个基数。—— HEIGHT BALANCED 等高直方图
基数 低于 254 的,直方图桶数为基数值。 —— FREQUENCY 频率直方图
12c :可以大于 254 个
