收集分区上的表统计信息可处理整个表

时间:2019-06-03 18:15:37

标签: oracle partitioning database-performance database-partitioning

我有一张约有20个分区的表。每个分区大约有1.9亿条记录。我需要在处理期间定期收集表的统计信息,这是使用DBMS_STATS.GATHER_TABLE_STATS命令进行的。当表只有1个分区时,通常需要4分钟才能完成。随着时间的流逝,分区的数量已经增加,并且collect_table_stats花费的时间也增加了。然后,我通过向GATHER_TABLE_STATS命令添加PARTNAME参数,切换到仅在分区上收集统计信息,但是花费的时间并未减少。我什至创建了一个只有1,000行的新分区,当我在该分区上收集统计信息时,仍需要22到25分钟才能完成。 我查看了USER_TAB_PARTITIONS表,发现LAST_ANALYZED列仅针对我在GATHER_TABLE_STATS中指定的分区进行了更新,因此我相信统计信息仅收集在我的单个分区上,但是为什么要这样做长? 如果有帮助,这是我的DDL。请注意,我正在创建本地索引。没有其他表对此表具有外键引用。

CREATE TABLE LAR_ALLOCATION_PER_PART (
  PROC_MONTH                DATE        NOT NULL,
  COUNTRY_CODE              VARCHAR2(2) NOT NULL,
  PART_NUMBER               VARCHAR2(20), 
  CUSTOMER_CODE             VARCHAR2(32),
  LAR_ID                    NUMBER     NOT NULL,
  GROSS_SALES_AMOUNT        NUMBER,
  ALLOCATION_AMOUNT         NUMBER,
  WARRANTY_AMOUNT           NUMBER,
  CURRENCY_CODE             VARCHAR2(5),
  CONSTRAINT LAR_ALLOC_PP_COUNTRY_CODE_FK FOREIGN KEY (COUNTRY_CODE) REFERENCES SUPPORTED_COUNTRY (COUNTRY_CODE),
  CONSTRAINT LAR_ALLOC_PP_PART_NUM_FK FOREIGN KEY (PART_NUMBER) REFERENCES PART_CLASSIFICATION (ODS_PART_NUMBER),
  CONSTRAINT LAR_ALLOC_PP_LAR_ID_FK FOREIGN KEY (LAR_ID) REFERENCES LEDGER_ALLOCATION_RULE (ID)
)
PARTITION BY RANGE(PROC_MONTH)
INTERVAL(NUMTOYMINTERVAL(1,'MONTH'))
(
  PARTITION prior2017 VALUES LESS THAN (TO_DATE('01-JAN-2017', 'DD-MON-YYYY'))
);

CREATE INDEX LAR_ALLOCATION_PER_PART_IDX
ON LAR_ALLOCATION_PER_PART
   (COUNTRY_CODE, LAR_ID, CUSTOMER_CODE, PART_NUMBER) LOCAL;

这是我用来收集统计信息的命令:

BEGIN 
    DBMS_STATS.GATHER_TABLE_STATS(OWNNAME  => 'MY_SCHEMA',
                                  TABNAME  => 'LAR_ALLOCATION_PER_PART',
                                  PARTNAME => 'SYS_P40553', --Jan 2020:  1,000 records
                                  OPTIONS  => 'GATHER AUTO',
                                  DEGREE   => DBMS_STATS.DEFAULT_DEGREE,
                                  CASCADE  => TRUE);
END;

我尝试将CASCADE设置为FALSE,将DEGREE设置为32,甚至将ESTIMATE_PERCENT值设置为10,但是对运行时间没有统计学上的显着影响。

我刚刚看到了有关使用增量统计信息的答案,我将尝试一下,但是我想了解为什么在分区上收集统计信息会花费这么长时间,以及我做错了什么。 / p>

1 个答案:

答案 0 :(得分:1)

您可能希望查看DBMS_STATS.GATHER_TABLE_STATS的{​​{3}}参数。

默认情况下,将收集分区和全局表的统计信息。将值更改为APPROX_GLOBAL AND PARTITION可以避免重新收集全局表的统计信息。


INCREMENTAL绝对是个好主意,应该可以提供很大的帮助。尽管增量统计有一些限制。例如,您必须使用默认的ESTIMATE_PERCENT进行收集。

收集单个分区的统计信息比较缓慢的原因是很长的话。

首先,优化器需要知道值的数量和不同值的数量。不同值的数量通常更有用。例如,如果我们查询select * from employee where employee_id = 1,Oracle可以查看EMPLOYEE_ID的不同性,确定谓词返回一行,并且索引将是完美的。另一方面,如果我们查询select * from employee where status = 'terminated',Oracle可以查看STATUS的不同性,确定谓词返回许多行,并且全表扫描是更好的匹配。

查找不同值的数量比查找值的总数要复杂得多。天真的算法会对值进行排序或散列,但这需要大量的时间和空间。取而代之的是,Oracle可以使用GRANULARITY之类的算法根据表的一次传递来估计值。这就是为什么需要将ESTIMATE_PERCENT保留为默认值的原因-扫描整个表比对表的10%进行排序要快。

但是,找到不同值的数量会使分区变得更加复杂。 Oracle需要知道每个分区以及整个表的不同值的数量。在单个分区中添加少量行可能会显着更改整个表的结果,这就是Oracle默认情况下必须重新扫描整个表的原因。

例如,考虑生日问题。想象一下,有一个BIRTHDAY列用于人群划分。如果分区A有15个不同的生日,分区B有15个不同的生日,那么整个表有多少个不同的生日?可能少于30。

增量统计信息通过为每个分区创建一个提要来解决该问题。这些概要可以快速组合在一起,以重新估计不同值的全局数量,而无需重新扫描每个分区。只需为每个分区存储一点额外的数据。