Oracle统计信息(二)—— 直方图 Histogram

一、 简介

1. 引入原因

在普通的统计信息中,CBO会默认目标列数据在其最小与最大值间是均匀分布的,并以此为依据预估条件选择率及结果集cardinality,进而选择执行计划。但在实际中,显然有些数据不是平均分布的,会出现所谓的“数据倾斜”,此时产生的执行计划很可能就不是最佳的。

 

2. 直方图是什么

直方图是一种特殊的列统计信息,它详细描述了列的数据分布情况。直方图收集后,CBO不会再认为列上是均匀分布的,可以根据实际情况预估条件选择率及结果集cardinality,选择正确的执行计划。

直方图使用Bucket(桶)的方式描述目标列的数据分布,每个Bucket就是一组,存放一个或多个列目标中的数据。oracle采用两个维度描述一个Bucket —— endpoint number和endpoint value(后文介绍)。两个维度信息,即直方图数据存储在HISTGRM$基表中,可以通过数据字典dba_tab_histgrams,dba_part_histgrams,dba_subpart_histgrams来查看表、分区及子分区的直方图信息。

 

二、 直方图的类型

12c之前,oracle有Frequency和Balanced两类直方图,12c又引入了Top-Frequency和Hybrid两类直方图。下面分别来看:

1. Frequency直方图

如何能详细描述列的数据分布情况?最简单的方法当然是统计每个列distinct值有多少并存储,实际上这就是oracle中的Frequency类型直方图。

对于这类直方图,列中有多少distinct值,数据字典中就会存储多少条记录。endpoint value记录这些distinct值,endpoint number则记录到此distinct值为止共有多少条记录。此时endpoint number是一个累加值,可以用一条记录的endpoint number减去前一条记录的endpoint number得到这条记录对应的endpoint value值的记录数。

这种方法简单易想,但是对于大表而言,它是不切实际的。12c之前,oracle要求Frequency类型直方图对应的Bucket数不超过254,即目标列distinct值不能超过254(12c去除了该限制)。

为了处理distinct值特别多的大表,oracle引入了Height Balanced类型直方图。

 

2. Height Balanced直方图

在12c之前,如果目标列distinct值超过了254,oracle会转为对目标列收集Height Balanced类型直方图。

对于这类直方图,oracle首先根据目标列对目标表的所有记录从小到大排序,然后用总记录数除以需要使用的Bucket数,来决定每个Bucket中需要描述的已排好序的记录数。

此时endpoint number记录的是Bucket号,Bucket号从0开始一直到N。0号Bucket对应的endpoint value记录目标列最小值,其他Bucket对应的endpoint value则记录到此Bucket为止目标列的最大值。

为节省空间,对于相邻的仅endpoint number不同而endpoint value相同的记录,oracle会在数据字典中合并存储。假如endpoint number=2和3的Bucket对应endpoint value都为P,数据字典中只会记录endpoint number=3,endpoint value=P。因此,Height Balanced类型直方图数据字典中的endpoint number可能是不连续的。

这种合并后记录在数据字典的endpoint value,oracle称为popular value。显然popular value记录的endpoint number值与上一条记录的endpoint number值相差越大,popular value在目标列中所占比例越大,对应的cardinality也就越大,这应该也是将其称为popular value的原因。

 

三、 直方图收集方法

1. 语法格式

对于dbms_stats包,是通过指定method_opt参数实现的,该参数可接受值如下:

for all [indexed | hidden] columns [size_clause]
for columns [size_clause] column | attribute [size_clause] [,column | attribute [size_clause]...]

其中size_clause必须符合以下格式

size [整数值 | repeat | auto | skewonly]
  • 整数值:直方图的Bucket数,范围为[1,254],为1表示删除列上直方图信息
  • repeat:只对已有直方图的列收集直方图信息
  • auto:由oracle决定是否对列收集直方图,以及使用哪类直方图。oracle默认只对用过的列(where条件中出现过的列)自动收集直方图统计信息,oracle会在sys.col_usage$基表中记录各列的使用情况。在自动收集直方图统计信息时先查该表,如果列未被使用过,则不会收集。
  • skewonly:只对倾斜列收集直方图

 

2. 例子

对表所有有索引的列以auto方式收集直方图

exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for 
 all indexed columns size auto');

对表的empno和deptno列以auto方式收集直方图

exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for 
 columns size auto empno deptno');

对表的empno和deptno列收集直方图,并指定Bucket数为10

exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for 
 columns size 10 empno deptno');

对表的empno和deptno列收集直方图,指定empno列Bucket数为10,deptno列Bucket数为5

exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for 
 columns empno size 10 deptno size 5');

删除empno列的直方图

exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for 
 columns empno size 1');

删除表上所有列的直方图

exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for 
 all columns size 1');

 

四、 注意事项

  • 直方图是为解决数据倾斜问题而引入,它对CBO选择度、基数估计及执行计划选择有直接影响。
  • 如果列数据分布均匀(例如有唯一索引的列),完全没必要对该列收集直方图。
  • 对于从没在where条件中出现或者出现非常少的列,无须对该列收集直方图。
  • 在配置10g中引进的自动统计信息收集作业时,需要特别注意对直方图的收集策略。
  • 直方图可能影响shared cursor能否被共享。若cursor_sharing值设为similar,而查询列有直方图,oracle会对列的每一个distinct值产生一个child cursor,这可能导致严重的硬解析,设置引发高版本问题。对于cursor_sharing参数,建议值还是使用默认的EXACT。
  • 对于字符型的字段,直方图有重大缺陷 —— oracle只会将字段的头32Byte取出来(实际上只取头15Byte),将其转换为一个浮点数,并将这个浮点数作为直方图信息存在数据字典里。对于超过32Byte而前32Byte又相同的字符列值,oracle会认为它们是完全相同的,即使它们并不相同,这很可能导致执行计划选择错误。案例参考:https://blog.csdn.net/Hehuyi_In/article/details/106162142
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章