一. 概述
对象统计信息描述数据是如何在数据库中存储的。比如,一张表里面有多少行数据,某一列数据的最大值是多少等等。这些信息有助于查询优化器找到正确高效的执行计划。举个例子,有这样一个场景,从一个特定地点回家,哪种交通方式最快捷?汽车,火车还是飞机?如果不知道我在哪里,我家在哪里,就得不到合理的答案。同样,如果没有对象统计信息,查询优化器也找不到正确高效的执行计划。
二. 可用的对象统计信息
有三种类型的对象统计信息可用:表统计,列统计和索引统计,下面举一个例子来说明一下这三种类型分别提供了什么样的统计信息。
1. 准备表数据
SET SERVEROUTPUT ON
DROP TABLE t;
execute dbms_random.seed(0)
CREATE TABLE t
AS
SELECT rownum AS id,
round(dbms_random.normal*1000) AS val1,
100+round(ln(rownum/3.25+2)) AS val2,
100+round(ln(rownum/3.25+2)) AS val3,
dbms_random.string('p',250) AS pad
FROM dual
CONNECT BY level <= 1000
ORDER BY dbms_random.value;
UPDATE t SET val1 = NULL WHERE val1 < 0;
ALTER TABLE t ADD CONSTRAINT t_pk PRIMARY KEY (id);
CREATE INDEX t_val1_i ON t (val1);
CREATE INDEX t_val2_i ON t (val2);
BEGIN
dbms_stats.gather_table_stats(ownname => user,
tabname => 'T',
estimate_percent => 100,
method_opt => 'for all columns size skewonly',
cascade => TRUE);
END;
/
最后这个存储过程的意思是收集当前用户表T的统计信息,各个参数的含义就不说明了。
2.查询表统计信息
SELECT num_rows, blocks, empty_blocks, avg_space, chain_cnt, avg_row_len
FROM user_tab_statistics
WHERE table_name = 'T';
结果如下图所示:
num_rows:表T中数据行数,总共1000行
blocks:高水位以下的数据块个数
empty_blocks:高水位以上的数据块个数(dbms_stats不计算这个值,被设置为0)
avg_space:表里数据块的平均空闲空间(dbms_stats不计算这个值,被设置为0)
chain_cnt:涉及行链接和行迁移的总行数(dbms_stats不计算这个值,被设置为0)
avg_row_len:表中平均每一个记录的长度(字节)
3. 查询列统计信息
COLUMN name FORMAT A4
COLUMN #dst FORMAT 9999
COLUMN low_value FORMAT A14
COLUMN high_value FORMAT A14
COLUMN dens FORMAT .99999
COLUMN #null FORMAT 9999
COLUMN avglen FORMAT 9999
COLUMN histogram FORMAT A15
COLUMN #bkt FORMAT 9999
SELECT column_name AS "NAME",
num_distinct AS "#DST",
low_value,
high_value,
density AS "DENS",
num_nulls AS "#NULL",
avg_col_len AS "AVGLEN",
histogram,
num_buckets AS "#BKT"
FROM user_tab_col_statistics
WHERE table_name = 'T';
结果如下图所示:
各列值的含义如下:
num_distinct(#DST):该列中唯一值的数量,比如val2列唯一值的数量为6.
low_value:该列中最小值,显示为内部存储格式。(需要转换才能看懂)
high_value:该列中最大值,显示为内部存储格式。(需要转换才能看懂)
density(DENS):0到1之间的小数,接近0表示对于列的过滤操作能去掉大多数的行。接近1表示对于该列的过滤操作起不到什么作用。如果没有直方图density=1/num_distinct,比如id列没有直方图,dens=1/1000=0.001。
num_nulls(#null):该列中存储null的总数。
avg_col_len(avglen):平均列大小,以字节表示
histogram:表示是否有直方图,none表示没有,frequency表示频率类型,height balanced表示平均分布类型。
num_buckets(#bkt):直方图里面的桶数,桶就是一组同类的数值放在一起。如果没有直方图,桶数为1,最大桶数为254.
查询优化器可以参考这里面的一些信息,来生成高效的执行计划。比如一个查询要按照val2列来过滤数据,查询优化器就会总和考虑val2列总共有多少个不同的值,最大值多少,最小值多少,在直方图里面的分布情况是怎样的。
4. 直方图
查询优化器原则上认为某一列上的数据是分布均匀的,比如id列,存储的是1到1000的数字,每个值一次。这种情况要找到满足条件的数据行(比如 id between 3 and 19),查询优化器只需要知道id列的最大值,最小值和唯一值总数即可估算大概要查询出多少数据。因为如果超过了20%的数据就不会走索引扫描,因为索引扫描在这种情况比全表扫描更慢。
但是,如果数据分布不均匀,那么就需要直方图来帮助查询优化器做出正确选择。比如val2列就是分布不均的,运行下面的SQL可以看出来
SELECT val2 AS val2, count(*)
FROM t
GROUP BY val2
ORDER BY val2;
结果如下图:
可以看出val2列总共只取6个值,取值105的最多有502个,取值101的最少,只有8个。
直方图存在两种:频度直方图(frequency histogram)和等高直方图(height-balanced histogram)。从列统计信息可以看出val2列属于频度直方图,模型可能如下图所示:
查看直方图信息SQL:
SELECT endpoint_value, endpoint_number,
endpoint_number - lag(endpoint_number,1,0)
OVER (ORDER BY endpoint_number) AS frequency
FROM user_tab_histograms
WHERE table_name = 'T'
AND column_name = 'VAL2'
ORDER BY endpoint_number;
结果如下图所示:
第一列:val2列的值,第二列:累计出现次数,第三列:该值出现次数。比如val2取值105的行数为502行,小于等于105的行数为788行。根据val2直方图信息,我们就很容易估算出val2取值在某一个范围内的行数。
当一列的唯一值数量大于桶允许的最大数254的时候,频度直方图就不适合了。这个时候需要使用等高直方图,这里就不介绍了。
5. 索引统计信息
SQL如下:
SELECT index_name AS name,
blevel,
leaf_blocks AS leaf_blks,
distinct_keys AS dst_keys,
num_rows,
clustering_factor AS clust_fact,
avg_leaf_blocks_per_key AS leaf_per_key,
avg_data_blocks_per_key AS data_per_key
FROM user_ind_statistics
WHERE table_name = 'T';
结果如下:
blevel:为了访问叶子块而需访问的分支块数
leaf_blocks:索引中的叶子块数
distinct_keys:索引中唯一键值总数
num_rows:索引中的键值数
clustering_factor:表明有多少临近的索引条目指到相邻的数据块(我也没看懂索引这里的统计信息是什么意思有什么作用,哪位朋友如果知道还望告知)