统计信息类似于战争中的侦察兵,如果情报工作没有做好,打仗就会输掉战争。同样的道理,如果没有正确地收集表的统计信息,或者没有及时地更新表的统计信息,SQL的执行计划就会跑偏,SQL也就会出现性能问题。收集统计信息是为了让优化器选择最佳执行计划,以最少的代价(成本)查询出表中的数据。
统计信息主要分为表的统计信息、列的统计信息、索引的统计信息、系统的统计信息、数据字典的统计信息以及动态性能视图基表的统计信息。
1.1、表的统计信息
表的统计信息主要包含表的总行数(num_rows)、表的块数(blocks)以及行平均长度(avg_row_len),我们可以通过查询数据字典DBA_TABLES获取表的统计信息。
创建一个测试表T_STATS:
create table t_stats as select * from DBA_OBJECTS;
我们查看表T_STATS常用的表的统计信息:
select owner, table_name, num_rows, blocks, avg_row_len from dba_tables where owner = 'SCOTT' and table_name = 'T_STATS';
因为T_STATS是新创建的表,没有收集过统计信息,所以从DBA_TABLES查询数据是空的。
现在我们来收集表T_STATS的统计信息。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T_STATS',
estimate_percent => 100,
method_opt => 'for all columns size auto',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
这段SQL代码是一个PL/SQL块,它调用了DBMS_STATS
包中的GATHER_TABLE_STATS
过程,用于收集指定表的统计信息。下面是对这段代码的详细分析:
-
BEGIN ... END;
:这是一个匿名PL/SQL块,用于执行一组SQL语句。在这个块中,你可以执行多个命令,它们作为一个整体事务来处理。 -
DBMS_STATS.GATHER_TABLE_STATS
:这是Oracle提供的一个过程,用于收集表的统计信息。统计信息对于优化器来说非常重要,因为优化器依赖这些信息来选择最佳的执行计划。 -
ownname => 'SCOTT'
:这个参数指定了要收集统计信息的表的所有者(schema)名称。在这个例子中,所有者名称是SCOTT
。 -
tabname => 'T_STATS'
:这个参数指定了要收集统计信息的表的名称。在这个例子中,表的名称是T_STATS
。 -
estimate_percent => 100
:这个参数指定了统计信息的收集方式。100
表示使用全表扫描来收集统计信息,这将提供最准确的统计数据,但可能需要更多的时间。如果设置为DBMS_STATS.AUTO_SAMPLE_SIZE
,优化器将自动决定采样的行数。 -
method_opt => 'for all columns size auto'
:这个参数指定了收集统计信息的方法。for all columns
表示为表中的所有列收集统计信息,size auto
表示让优化器自动决定每个列的统计信息收集的粒度。 -
no_invalidate => FALSE
:这个参数指定在收集统计信息后是否使表的统计信息保持有效。FALSE
表示收集统计信息后,如果表的数据发生了变化,将使统计信息无效,以便在下次查询时重新收集。 -
degree => 1
:这个参数指定了用于收集统计信息的并行度。1
表示使用单线程(非并行)来收集统计信息。如果你的系统有多核处理器,增加这个值可能会加快统计信息收集的速度。 -
cascade => TRUE
:这个参数指定是否递归地收集依赖于该表的所有对象(如索引、视图、序列等)的统计信息。
总的来说,这段SQL代码的作用是为SCOTT
用户下的T_STATS
表收集详尽的统计信息,以便优化器可以更准确地评估查询成本并选择最佳的执行计划。通过设置estimate_percent
为100
,确保了统计信息的准确性,同时通过cascade
参数,确保了所有相关对象的统计信息也是最新的。
我们再次查看表的统计信息:
select owner, table_name, num_rows, blocks, avg_row_len from dba_tables where owner = 'SCOTT' and table_name = 'T_STATS';
从查询中我们可以看到,表T_STATS一共有76508行数据,1115个数据块,平均行长度为98字节。
1.2、列的统计信息
列的统计信息主要包含列的基数、列中的空值数量以及列的数据分布情况(直方图)。我们可以通过数据字典DBA_TAB_COL_STATISTICS查看列的统计信息。
现在我们查看表T_STATS常用的列统计信息:
select column_name, num_distinct, num_nulls, num_buckets, histogram
from dba_tab_col_statistics
where owner = 'SCOTT'
and table_name = 'T_STATS';
上面查询中,第一个列表示列名字,第二个列表示列的基数,第三个列表示列中NULL值的数量,第四个列表示直方图的桶数,最后一个列表示直方图类型。
在工作中,我们经常使用下面脚本查看表和列的统计信息:
select a.column_name,
b.num_rows,
a.num_nulls,
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 = 'SCOTT'
and a.table_name = 'T_STATS';
1.3、索引的统计信息
索引的统计信息主要包含索引blevel(索引高度-1)、叶子块的个数(leaf_blocks)以及集群因子(clustering_factor)。我们可以通过数据字典DBA_INDEXES查看索引的统计信息。
我们在OBJECT_ID列上创建一个索引:
create index idx_t_stats_id on t_stats(object_id);
创建索引的时候会自动收集索引的统计信息,运行下面脚本查看索引的统计信息:
select BLEVEL,LEAF_BLOCKS,CLUSTERING_FACTOR,STATUS from DBA_INDEXES where OWNER='SCOTT' and INDEX_NAME='IDX_T_STATS_ID';
如果要单独对索引收集统计信息,可以使用下面脚本收集:
BEGIN
DBMS_STATS.GATHER_INDEX_STATS(ownname => 'SCOTT',
indname => 'IDX_T_STATS_ID');
END;