Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2796826
  • 博文数量: 389
  • 博客积分: 4177
  • 博客等级: 上校
  • 技术积分: 4773
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-16 23:29
文章分类

全部博文(389)

分类: Oracle

2013-12-09 11:58:01

                                               ORACLE直方图

    为什么需要直方图 ?当表中一列数据比较的值分布比较均匀时,optimzer可以很好的通过最大值,最小值和NDV(唯一值的个数)
就可以判断出cardinality.对于cardinality越精确,optimzer就可以更加好的选择执行计划
  
  创建一个表

SQL> create table frank.t2(a int,b varchar2(100));

Table created.

分别执行insert into frank.t2 values(1,'abcd')和insert into frank.t2 values(2,'efg'), 然后收集统计信息

 
SQL> begin
  2     dbms_stats.gather_table_stats(tabname => 'T2',ownname => 'FRANK',method_opt => 'FOR ALL COLUMNS SIZE 1');
  3     end;
  4  /

--FOR ALL COLUMNS SIZE 1 不收集直方图信息

执行一个语句来看看optimizer评估的行
SQL>explain plan for select * from t2 where a=1;

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   100 |   700 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T2   |   100 |   700 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

返回100行,说明优化器在这种数据平均分布的情况下评估很准确.

现在insert into frank.t2 values(3,'mnb') 一行,人为的模拟数据分布不均,再次收集统计信息

SQL> explain plan for select * from t2 where a=3;

Explained.


PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1513984157

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    67 |   469 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T2   |    67 |   469 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

优化器评估为67行.计算公式为 rows/ndv=(200/3)=66.66666

看看收集了集方图后的结果
SQL> exec dbms_stats.gather_table_stats(tabname => 'T2',ownname => 'FRANK',method_opt => 'FOR ALL COLUMNS SIZE AUTO');

PL/SQL procedure successfully completed.

SQL>  explain plan for select * from t2 where a=3;

Explained.
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1513984157

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     7 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T2   |     1 |     7 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

可以看出通过增加了直方图,oracle比较准确的评估了cardinality。

SQL> select column_name,histogram from user_tab_col_statistics where table_name='T2';

COLUMN_NAME                    HISTOGRAM
------------------------------ ---------------
A                              FREQUENCY       --频率直方图
B                              NONE

直方图分为两种频率直方图和高度平衡直方图

直方图的限制:1,收集直方图有开销,如cpu和磁盘空间;2,对于每个栏位超过254的distinct value,频率直方图的作用开始下降
随着NDV的增加,精度进一步下降,这时候只能使用高度平衡直方图.3,对于字符类型,只能收集前32个字节;
4,在非索引的栏位上收集直方图的效果有限.


高度平衡和频率直方图的选择:对于某个栏位的NDV小于所定义的桶数,使用频率直方图,否则使用高度平衡直方图。两种方式的最大
的桶数为254,

SQL> create table t3(a int);

Table created.

SQL> select count(distinct a) from t3;  --insert 76种不同的值

COUNT(DISTINCTA)
----------------
              76

SQL> exec dbms_stats.gather_table_stats(tabname => 'T3',ownname => 'FRANK',method_opt => 'FOR COLUMNS A SIZE 75');

人为的定义桶数小于NDV,在这种条件,oracle会使用高度平衡直方图,因为频率直方图75个bucket容不下76

SQL>  select column_name,histogram from user_tab_col_statistics where table_name='T3';

COLUMN_NAME                    HISTOGRAM
------------------------------ ---------------
A                              HEIGHT BALANCED

 


对于频率直方图,如果NDV小于254的情况,ndv应该是和桶数相等的.有些bug会产生不一致,导致评估不准确,具体可以参考metalink
的相关bug。

SQL> select count(b.endpoint_value)
  2  from user_histograms b
  3  where table_name='T2'
  4  and column_name='A'
  5  ;

COUNT(B.ENDPOINT_VALUE)
-----------------------
                      3

SQL> select table_name,column_name,num_distinct from user_tab_col_statistics
  2  where table_name='T2' and column_name='A';

TABLE_NAME                     COLUMN_NAME                    NUM_DISTINCT
------------------------------ ------------------------------ ------------
T2                             A                                         3

 

一般建议的收集方法为'FOR ALL COLUMNS SIZE AUTO',除非有很好的理由去更改,由oracle自行决定是否需要histogram和桶数

阅读(6082) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~