我们通常使用下面脚本收集表和索引的统计信息。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TAB_OWNER',
tabname => 'TAB_NAME',
estimate_percent => 根据表大小设置,
method_opt => 'for all columns size repeat',
no_invalidate => FALSE,
degree => 根据表大小,CPU 资源和负载设置,
granularity => 'AUTO',
cascade => TRUE);
END;
ownname
表示表的拥有者,不区分大小写。
tabname
表示表名字,不区分大小写。
granularity
表示收集统计信息的粒度,该选项只对分区表生效,默认为 AUTO,表示让 Oracle 根据表的分区类型自己判断如何收集分区表的统计信息。对于该选项,我们一般采用 AUTO 方式,也就是数据库默认方式,因此,在后面的脚本中,省略该选项。
estimate_percent
表示采样率,范围是 0.000 001~100。
我们一般对小于 1GB 的表进行 100% 采样,因为表很小,即使 100% 采样速度也比较快。有时候小表有可能数据分布不均衡,如果没有 100% 采样,可能会导致统计信息不准。因此我们建议对小表 100% 采样。
我们一般对表大小在 1GB~5GB 的表采样 50%,对大于 5GB 的表采样 30%。如果表特别大,有几十甚至上百 GB,我们建议应该先对表进行分区,然后分别对每个分区收集统计信息。
一般情况下,为了确保统计信息比较准确,我们建议采样率不要低于 30%。
我们可以使用下面脚本查看表的采样率。
SELECT owner,
table_name,
num_rows,
sample_size,
round(sample_size / num_rows * 100) estimate_percent
FROM DBA_TAB_STATISTICS
WHERE owner='SCOTT' AND table_name='T_STATS';
OWNER TABLE_NAME NUM_ROWS SAMPLE_SIZE ESTIMATE_PERCENT
--------------- --------------- ---------- ----------- ----------------
SCOTT T_STATS 72674 72674 100
从上面查询我们可以看到,对表 T_STATS 是 100% 采样的。现在我们将采样率设置为 30%。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T_STATS',
estimate_percent => 30,
method_opt => 'for all columns size auto',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
PL/SQL procedure successfully completed.
SELECT owner,
table_name,
num_rows,
sample_size,
round(sample_size / num_rows * 100) estimate_percent
FROM DBA_TAB_STATISTICS
WHERE owner='SCOTT' AND table_name='T_STATS';
OWNER TABLE_NAME NUM_ROWS SAMPLE_SIZE ESTIMATE_PERCENT
--------------- --------------- ---------- ----------- ----------------
SCOTT T_STATS 73067 21920 30
从上面查询我们可以看到采样率为 30%,表的总行数被估算为 73 067,而实际上表的总行数为 72 674。设置采样率 30% 的时候,一共分析了 21 920 条数据,表的总行数等于 round(21 920*100/30),也就是 73 067。
除非一个表是小表,否则没有必要对一个表 100% 采样。因为表一直都会进行 DML 操作,表中的数据始终是变化的。
method_opt
用于控制收集直方图策略。
method_opt => 'for all columns size 1'
表示所有列都不收集直方图,如下所示。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T_STATS',
estimate_percent => 100,
method_opt => 'for all columns size 1',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
PL/SQL procedure successfully completed.
我们查看直方图信息。
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';
COLUMN_NAME NUM_ROWS NUM_NULLS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS
--------------- ---------- ---------- ----------- ----------- --------------- -------
EDITION_NAME 72674 72674 0 0 NONE 0
NAMESPACE 72674 1 21 .03 NONE 1
SECONDARY 72674 0 2 0 NONE 1
GENERATED 72674 0 2 0 NONE 1
TEMPORARY 72674 0 2 0 NONE 1
STATUS 72674 0 2 0 NONE 1
TIMESTAMP 72674 1 1592 2.19 NONE 1
LAST_DDL_TIME 72674 1 1521 2.09 NONE 1
CREATED 72674 0 1472 2.03 NONE 1
OBJECT_TYPE 72674 0 45 .06 NONE 1
DATA_OBJECT_ID 72674 64833 7796 10.73 NONE 1
OBJECT_ID 72674 1 72673 100 NONE 1
SUBOBJECT_NAME 72674 72145 140 .19 NONE 1
OBJECT_NAME 72674 0 44333 61 NONE 1
OWNER 72674 0 31 .04 NONE 1
15 rows selected.
从上面查询我们看到,所有列都没有收集直方图。
method_opt => 'for all columns size skewonly'
表示对表中所有列收集自动判断是否收集直方图,如下所示。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T_STATS',
estimate_percent => 100,
method_opt => 'for all columns size skewonly',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
PL/SQL procedure successfully completed.
我们查看直方图信息,如下所示。
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';
COLUMN_NAME NUM_ROWS NUM_NULLS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS
--------------- ---------- ---------- ----------- ----------- --------------- -------
EDITION_NAME 72674 72674 0 0 NONE 0
NAMESPACE 72674 1 21 .03 FREQUENCY 21
SECONDARY 72674 0 2 0 FREQUENCY 2
GENERATED 72674 0 2 0 FREQUENCY 2
TEMPORARY 72674 0 2 0 FREQUENCY 2
STATUS 72674 0 2 0 FREQUENCY 2
TIMESTAMP 72674 1 1592 2.19 HEIGHT BALANCED 254
LAST_DDL_TIME 72674 1 1521 2.09 HEIGHT BALANCED 254
CREATED 72674 0 1472 2.03 HEIGHT BALANCED 254
OBJECT_TYPE 72674 0 45 .06 FREQUENCY 45
DATA_OBJECT_ID 72674 64833 7796 10.73 HEIGHT BALANCED 254
OBJECT_ID 72674 1 72673 100 NONE 1
SUBOBJECT_NAME 72674 72145 140 .19 FREQUENCY 140
OBJECT_NAME 72674 0 44333 61 HEIGHT BALANCED 254
OWNER 72674 0 31 .04 FREQUENCY 31
15 rows selected.
从上面查询我们可以看到,除了 OBJECT_ID 列和 EDITION_NAME 列,其余所有列都收集了直方图。因为 EDITION_NAME 列全是 NULL,所以没必要收集直方图。OBJECT_ID 列选择性为 100%,没必要收集直方图。
在实际工作中千万不要使用**method_opt => 'for all columns size skewonly'**
收集直方图信息,因为并不是表中所有的列都会出现在 where 条件中,对没有出现在 where 条件中的列收集直方图没有意义。
method_opt => 'for all columns size auto'
表示对出现在 where 条件中的列自动判断是否收集直方图。
现在我们删除表中所有列的直方图。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T_STATS',
estimate_percent => 100,
method_opt => 'for all columns size 1',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
PL/SQL procedure successfully completed.
我们执行下面 SQL,以便将 owner 列放入 where 条件中。
select count(*) from t_stats where owner='SYS';
COUNT(*)
----------
30850
接下来我们刷新数据库监控信息。
begin
dbms_stats.flush_database_monitoring_info;
end;
PL/SQL procedure successfully completed.
我们使用method_opt => 'for all columns size auto'
方式对表收集统计信息。
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;
PL/SQL procedure successfully completed.
然后我们查看直方图信息。
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';
COLUMN_NAME NUM_ROWS NUM_NULLS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS
--------------- ---------- ---------- ----------- ----------- --------------- -------
EDITION_NAME 72674 72674 0 0 NONE 0
NAMESPACE 72674 1 21 .03 NONE 1
SECONDARY 72674 0 2 0 NONE 1
GENERATED 72674 0 2 0 NONE 1
TEMPORARY 72674 0 2 0 NONE 1
STATUS 72674 0 2 0 NONE 1
TIMESTAMP 72674 1 1592 2.19 NONE 1
LAST_DDL_TIME 72674 1 1521 2.09 NONE 1
CREATED 72674 0 1472 2.03 NONE 1
OBJECT_TYPE 72674 0 45 .06 NONE 1
DATA_OBJECT_ID 72674 64833 7796 10.73 NONE 1
OBJECT_ID 72674 1 72673 100 NONE 1
SUBOBJECT_NAME 72674 72145 140 .19 NONE 1
OBJECT_NAME 72674 0 44333 61 NONE 1
OWNER 72674 0 31 .04 FREQUENCY 31
15 rows selected.
从上面查询我们可以看到,Oracle 自动地对 owner 列收集了直方图。
思考,如果将选择性比较高的列放入 where 条件中,会不会自动收集直方图?现在我们将 OBJECT_NAME 列放入 where 条件中。
select count(*) from t_stats where object_name='EMP';
COUNT(*)
----------
3
然后我们刷新数据库监控信息。
begin
dbms_stats.flush_database_monitoring_info;
end;
PL/SQL procedure successfully completed.
我们收集统计信息。
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;
PL/SQL procedure successfully completed.
我们查看 OBJECT_NAME 列是否收集了直方图。
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';
COLUMN_NAME NUM_ROWS NUM_NULLS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS
--------------- ---------- ---------- ----------- ----------- --------------- -------
EDITION_NAME 72674 72674 0 0 NONE 0
NAMESPACE 72674 1 21 .03 NONE 1
SECONDARY 72674 0 2 0 NONE 1
GENERATED 72674 0 2 0 NONE 1
TEMPORARY 72674 0 2 0 NONE 1
STATUS 72674 0 2 0 NONE 1
TIMESTAMP 72674 1 1592 2.19 NONE 1
LAST_DDL_TIME 72674 1 1521 2.09 NONE 1
CREATED 72674 0 1472 2.03 NONE 1
OBJECT_TYPE 72674 0 45 .06 NONE 1
DATA_OBJECT_ID 72674 64833 7796 10.73 NONE 1
OBJECT_ID 72674 1 72673 100 NONE 1
SUBOBJECT_NAME 72674 72145 140 .19 NONE 1
OBJECT_NAME 72674 0 44333 61 NONE 1
OWNER 72674 0 31 .04 FREQUENCY 31
15 rows selected.
从上面查询我们可以看到,OBJECT_NAME 列没有收集直方图。由此可见,使用 AUTO 方式收集直方图很智能。mothod_opt 默认的参数就是 for all columns size auto。
method_opt => 'for all columns size repeat'
表示当前有哪些列收集了直方图,现在就对哪些列收集直方图。
当前只对 OWNER 列收集了直方图,现在我们使用 REPEAT 方式收集直方图。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T_STATS',
estimate_percent => 100,
method_opt => 'for all columns size repeat',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
PL/SQL procedure successfully completed.
我们查看直方图信息。
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';
COLUMN_NAME NUM_ROWS NUM_NULLS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS
--------------- ---------- ---------- ----------- ----------- --------------- -------
EDITION_NAME 72674 72674 0 0 NONE 0
NAMESPACE 72674 1 21 .03 NONE 1
SECONDARY 72674 0 2 0 NONE 1
GENERATED 72674 0 2 0 NONE 1
TEMPORARY 72674 0 2 0 NONE 1
STATUS 72674 0 2 0 NONE 1
TIMESTAMP 72674 1 1592 2.19 NONE 1
LAST_DDL_TIME 72674 1 1521 2.09 NONE 1
CREATED 72674 0 1472 2.03 NONE 1
OBJECT_TYPE 72674 0 45 .06 NONE 1
DATA_OBJECT_ID 72674 64833 7796 10.73 NONE 1
OBJECT_ID 72674 1 72673 100 NONE 1
SUBOBJECT_NAME 72674 72145 140 .19 NONE 1
OBJECT_NAME 72674 0 44333 61 NONE 1
OWNER 72674 0 31 .04 FREQUENCY 31
15 rows selected.
从查询中我们可以看到,使用 REPEAT 方式延续了上次收集直方图的策略。对一个运行稳定的系统,我们应该采用 REPEAT 方式收集直方图。
单独对某一列收集直方图:method_opt => 'for columns 列名 size skewonly'
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T',
estimate_percent => 100,
method_opt => 'for columns SYS_STUNA$6DVXJXTP05EH56DTIR0X size skewonly',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
method_opt => 'for columns object_type size skewonly'
表示单独对 OBJECT_TYPE 列收集直方图,对于其余列,如果之前收集过直方图,现在也收集直方图。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'T_STATS',
estimate_percent => 100,
method_opt => 'for columns object_type size skewonly',
no_invalidate => FALSE,
degree => 1,
cascade => TRUE);
END;
PL/SQL procedure successfully completed.
我们查看直方图信息。
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';
COLUMN_NAME NUM_ROWS NUM_NULLS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS
--------------- ---------- ---------- ----------- ----------- --------------- -------
EDITION_NAME 72674 72674 0 0 NONE 0
NAMESPACE 72674 1 21 .03 NONE 1
SECONDARY 72674 0 2 0 NONE 1
GENERATED 72674 0 2 0 NONE 1
TEMPORARY 72674 0 2 0 NONE 1
STATUS 72674 0 2 0 NONE 1
TIMESTAMP 72674 1 1592 2.19 NONE 1
LAST_DDL_TIME 72674 1 1521 2.09 NONE 1
CREATED 72674 0 1472 2.03 NONE 1
OBJECT_TYPE 72674 0 45 .06 FREQUENCY 45
DATA_OBJECT_ID 72674 64833 7796 10.73 NONE 1
OBJECT_ID 72674 1 72673 100 NONE 1
SUBOBJECT_NAME 72674 72145 140 .19 NONE 1
OBJECT_NAME 72674 0 44333 61 NONE 1
OWNER 72674 0 31 .04 FREQUENCY 31
15 rows selected.
从查询中我们可以看到,OBJECT_TYPE 列收集了直方图,因为之前收集过 owner 列直方图,现在也跟着收集了 owner 列的直方图。
在实际工作中,我们需要对列收集直方图就收集直方图,需要删除某列直方图就删除其直方图,当系统趋于稳定之后,使用 REPEAT 方式收集直方图。
no_invalidate
表示共享池中涉及到该表的游标是否立即失效,默认值为 DBMS_STATS. AUTO_INVALIDATE
,表示让 Oracle 自己决定是否立即失效。我们建议将 no_invalidate 参数设置为 FALSE,立即失效。因为我们发现有时候 SQL 执行缓慢是因为统计信息过期导致,重新收集了统计信息之后执行计划还是没有更改,原因就在于没有将这个参数设置为 false。
degree
表示收集统计信息的并行度,默认为 NULL。如果表没有设置 degree,收集统计信息的时候后就不开并行;如果表设置了 degree,收集统计信息的时候就按照表的 degree 来开并行。可以查询 DBA_TABLES.degree
来查看表的 degree,一般情况下,表的 degree 都为 1。我们建议可以根据当时系统的负载、系统中 CPU 的个数以及表大小来综合判断设置并行度。
SELECT DEGREE FROM DBA_TABLES WHERE owner = 'SCOTT' AND table_name = 'T_STATS';
cascade
表示在收集表的统计信息的时候,是否级联收集索引的统计信息,默认值为 DBMS_STATS.AUTO_CASCADE
,表示让 Oracle 自己判断是否级联收集索引的统计信息。我们一般将其设置为 TRUE,在收集表的统计信息的时候,级联收集索引的统计信息。