ä¸?.何谓直方图:
直方图是ä¸?种统计å?上的工具,并非Oracle专有。é?š常用于对è?管理对象的某ä¸?–¹面的质量情况进è?管理,é?š常情况下它会表现为ä¸?种几何图形表,这ä¸?›¾形表æ˜? ¹æ�?»Ž实际çŽ??ä¸?‰€收集来的è¢??理å?象某ä¸?–¹面的质量分布情况的数æ�?‰€绘制成的,é?š常会画成以数量为底边,以é?度为高度的一系列连接起来的矩形图,因此直方图在统计å?上也称为质量分布图ã?‚比如下图所示,æ˜?¸€ä¸?»¥关å?生化学è?ƒ试成绩分数分布情况绘制的直方图ï¼?/p>
äº?Oracleä¸?›´方图的作ç”?¼š
既然直方图是ä¸?种å?è¢??理å?象某ä¸?方面质量进è?管理的描述工具,那么在Oracleä¸?‡ª然它也是对Oracleä¸?Ÿ�ä¸??象质量的描述工具,这ä¸??象就是Oracleä¸?œ€重è?的东西â?”â?”â?œ数æ�??�ã??/p>
在Oracleä¸?›´方图æ˜?¸€种å?数据分布质量情况进è?描述的工具ã?‚它会按照某ä¸?列不同å?¼出现数量å?少,以及出现的é?率高低来绘制数据的分布情况,以便能å?指å?优化器根æ�?•°æ�?š„分布做出正确的é?‰择。在某些情况下,表的列中的数值分布将会影响优化器使用索引还是执è?全表æ‰?��的决策ã?‚当 where 子句的å?¼具有不成比例数量的数å?¼时,将出现这ç?情况,使得全表扫描比索引访问的成æœ?›´低ã?‚这种情况下如果where 子句的过滤谓词列之上上有ä¸?ä¸?�ˆ理的正确的直方图,将会å?优化器做出æ?ç¡?š„选择发挥巨大的作ç”?¼Œ使得SQLè¯?�¥执è?成本æœ?低从而提升æ?§能ã€?/p>
ä¸?Oracleä¸?½¿用直方图的场合:
在分析表或索引时,直方图用于记录数据的分布ã?‚é?š过获得该信æ�?¼Œ基于成本的优 化器就可以决定使用将返回少量行的索引,è?Œ避免使用基于限制条件返回è?多è?的索引ã?‚直方图的使用不受索引的限制,可以在表的任何列上构建直方图ã??/p>
构é? 直方图æœ?主è?的原因就æ˜?¸®助优化器在表ä¸?•°æ�?¸¥重偏斜时做出更好的è?划:例å?,å?果一到两ä¸??¼构成了表中的大部分数据(数据偏斜),相关的索引就可能无法帮助减少满足查询所éœ?的I/O数量。创建直方图å�?»¥让基于成æœ?š„优化器知道何时使用索引才æœ?合é?‚,或何时应该根据WHERE子句ä¸?š„值返回表ä¸?0%的记录ã€?/p>
通常情况下在以下场合ä¸?»ºè®?½¿用直方图ï¼?/p>
ï¼?)ã?�当Where子句引用了列值分布存在明显偏å·?š„列时:当这ç?偏差相当明显时,以至äº?WHERE 子句ä¸?š„值将会使优化器é?‰择不同的执行è?划ã?‚这时应该使用直方图来帮助优化器来修正执行路径ã?‚(注意:å?果查è¯?¸�引用该列,则创建直方图没有意义ã?‚这种错è¯?¾ˆ常è?,è?å¤?DBA 会在偏差列上创建柱状图,即使没有任何查è?引用该列。)
ï¼?)ã?�当列å?¼å?致不正确的判æ–?—¶:这种情况é?š常会发生在多表连接时,例å?,假设我ä»?œ‰ä¸?ä¸?º”项的表联接,其结果集å�?œ‰ 10 行ã?‚Oracle 将会以一种使ç¬?¸€ä¸?�”接的结果集(集合基数)尽å�?ƒ½小的方式将表联接起来。é?š过在中间结果集ä¸?�º带更少的负载,查询将会运行得更快。为了使ä¸?—´结果æœ?小化,优化器尝试åœ?SQL 执è?的分析阶段评估每ä¸?»“果集的集合基数ã?‚在偏差的列上拥有直方图将会极大地帮助优化器作出正确的决策ã?‚å?优化器å?ä¸?—´结果集的大小作出不æ?ç¡?š„判断,它å�?ƒ½会é?‰择ä¸?种未达到æœ?优化的表联接方法。因此向该列添加直方图经常会向优化器提供使用æœ?佳联接方法所éœ?的信æ�???/p>
å›?直方图有两ç?类别,等频直方图与等高直方图ã€?/p>
默è?的,如果ä¸?ä¸??¾斜列上的唯ä¸?值超过了254ä¸?¼Œ那么ORACLE会å?此列建立等高直方图,否则建立等é?直方图ã??/p>
通过如下方式,建立表TAB,更新字段B,è?列B产生倾斜。并在B列上创建索引ã€?/p>
SQL> spool d:\hist.txt
SQL> create table tab (a number, b number);
表已创建ã€?/p>
SQL>
SQL> begin
2 for i in 1..10000 loop
3 insert into tab values (i, i);
4 end loop;
5 commit;
6 end;
7 /
PL/SQL 过程已成功完成ã??/p>
SQL> update tab set b=5 where b between 6 and 9995;
已更æ–?990行ã??/p>
SQL> commit;
提交完成ã€?/p>
SQL> create index ix_tab_b on tab(b);
索引已创建ã??/p>
然后分析è¡?¼Œ强制使列B不产生直方图ã€?/p>
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'SCOTT',
TABNAME => 'TAB',
CASCADE => TRUE,
METHOD_OPT => 'FOR COLUMNS B SIZE 1 ');
END;
查看视图USER_TAB_HISTOGRAMS,列B上只有最大å?¼,æœ?小å?¼两条è?录分åˆ??应ç?点号(endpoint_numberï¼?å’?,这种显示è?明列B没有直方图信æ�???/p>
SQL>SELECT table_name,column_name,endpoint_number,endpoint_value FROM USER_TAB_HISTOGRAMS WHERE TABLE_NAME='TAB' ï¼?/p>
TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
------------------------------ ---------------------------------------- --------------- --------------
TAB B 0 1
TAB B 1 10000
在没有直方图的情况下,在B列上进è?等å?¼查询的时å?™,都是索引范围æ‰?��ã€?/p>
SQL> select * from tab where b=1;
执è?计划
----------------------------------------------------------
Plan hash value: 439197569
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 6000 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB | 1000 | 6000 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TAB_B | 1000 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
SQL> select * from tab where b=5;
已é?‰择9991行ã??/p>
执è?计划
----------------------------------------------------------
Plan hash value: 439197569
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 6000 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB | 1000 | 6000 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TAB_B | 1000 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
收集直方图信æ�??‚看看是ä»?么效果ã?‚由于列Bå”?¸€值的ä¸?•°没有超过254因æ?产生的是等é?直方图ã??/p>
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'SCOTT',
TABNAME => 'TAB',
CASCADE => TRUE,
METHOD_OPT => 'FOR ALL COLUMNS SIZE AUTO ');
END;
在B=1时å?™采用索引扫描,而B=5时å?™,已经采用全表æ‰?��了,说明直方图起了作用ã??/p>
SQL> select * from tab where b=1;
执è?计划
----------------------------------------------------------
Plan hash value: 439197569
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB | 1 | 6 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TAB_B | 1 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
SQL> select * from tab where b=5;
已é?‰择9991行ã??/p>
执è?计划
----------------------------------------------------------
Plan hash value: 1995730731
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9991 | 59946 | 6 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TAB | 9991 | 59946 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------
查看此时的直方图信息ï¼?/p>
SQL>SELECT TABLE_NAME, COLUMN_NAME, ENDPOINT_NUMBER, ENDPOINT_VALUE FROM USER_TAB_HISTOGRAMS
WHERE TABLE_NAME = 'TAB'ï¼?/p>
TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
------------------------------ ---------------------------------------- --------------- --------------
TAB B 1 1
TAB B 2 2
TAB B 3 3
TAB B 4 4
TAB B 9995 5
TAB B 9996 9996
TAB B 9997 9997
TAB B 9998 9998
TAB B 9999 9999
TAB B 10000 10000
其中EDNPOINT_NUMBERæ˜?´¯计å?¼ã?‚EDNPOINT_VALUEæ˜?ˆ—的å?¼ã?‚可以看出这种等频直方图统è?的列的信æ�?˜¯非常精确的ã?‚它为每ä¸?ä¸?ˆ—值分配了ä¸?ä¸?¡¶。从执è?计划的ROWS部分也可以看出ORACLE计算出来的cardinalityæ˜?991,和实际的情况完全吻合ã??/p>
如果想知道每ä¸?ä¸?ˆ—值å?应的数量æ˜??少,éœ?要做ä¸?下简单的减法运算ï¼?/p>
假å?想知道列值等äº?的个数,那么å�?»¥通过ï¼?/p>
9995-4=9991得到。这就是ENDPOINT_NUMBERç´??值的å�?¹‰ã€?/p>
在看看等高直方图的情况ã??/p>
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'SCOTT',
TABNAME => 'TAB',
CASCADE => TRUE,
METHOD_OPT => 'FOR COLUMNS B SIZE 8 ');
END;
由于列Bæœ?0ä¸?”¯ä¸?值,通过上面的size 8å�?»¥强制ORACLE使用等高直方图ã??/p>
查看直方图信æ�?
SQL>SELECT TABLE_NAME, COLUMN_NAME, ENDPOINT_NUMBER, ENDPOINT_VALUE FROM USER_TAB_HISTOGRAMS
WHERE TABLE_NAME = 'TAB'ï¼?/p>
TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
------------------------------ ---------------------------------------- --------------- --------------
TAB B 0 1
TAB B 7 5
TAB B 8 10000
从查询结果惊奇的发现å�?œ‰三个æ¡? 7 8,原æ�?RACLE会自动省去EDNPOINT_VALUE值相同且ENDPOINT_NUMBER相邻的桶的å?¼ã??/p>
省去了桶(EDNPOINT_NUMBER)为1 2 3 4 5 6 ,EDNPOINT_VALUEä¸?的六条内容ã??/p>
说明:在等高直方图中,EDNPOINT_NUMBER代表桶号,这一点与等é?直方图不同ã??/p>
再看等高直方图下的执行è?划:
SQL> select * from tab where b=5;
已é?‰择9991行ã??/p>
执è?计划
----------------------------------------------------------
Plan hash value: 1995730731
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9982 | 59892 | 6 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TAB | 9982 | 59892 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------
有没有发现什么?
执è?计划的ROWS部分,ORACLE计算出来的cardinality不是特别精确的ã??991才是精确值ã?‚è?Œ等频直方图å�?»¥精确åˆ?991,因此可以è?等é?直方图比等高直方图稳定,精确ã€?/p>
å�?˜¯现实很å?时å?™,列的å”?¸€值是超过254的ã?‚只能使用等高直方图了ã??/p>