收集 table 分区处理整个 table 的统计信息

gather table stats on a partition processes entire table

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

有外键引用
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,但对 运行 时间没有任何统计显着影响。

我刚刚看到一个关于使用增量统计信息的回答,我打算尝试一下,但我想了解为什么在分区上收集统计信息需要这么长时间,以及我是否做错了什么.

您可能需要查看 DBMS_STATS.GATHER_TABLE_STATSGRANULARITY 参数。

默认情况下,会为分区和全局收集统计信息 table。将值更改为 APPROX_GLOBAL AND PARTITION 可能会避免全局 table.

的 re-gathering 统计信息

INCREMENTAL 绝对是个好主意,应该能提供很大帮助。虽然增量统计有一些限制。例如,您必须使用默认值 ESTIMATE_PERCENT.

进行收集

单分区统计慢的原因说来话长

首先优化器需要知道值的数量和不同值的数量。不同值的数量通常更有用。比如我们查询select * from employee where employee_id = 1,Oracle可以查看EMPLOYEE_ID的distinctness,判断谓词returns一行,索引就完美了。另一方面,如果我们查询 select * from employee where status = 'terminated',Oracle 可以查看 STATUS 的区别,确定谓词 returns 多行,并且完整 table 扫描是更好的匹配。

查找不同值的数量比查找值的总数更复杂。天真的算法会对值进行排序或散列,但这需要大量时间和 space。相反,Oracle 可以使用像 HyperLogLog 这样的算法来估计基于 table 的单次传递的值。这就是为什么您需要将 ESTIMATE_PERCENT 保持为默认值 - 扫描整个 table 比对 table.

的 10% 进行排序更快

但是通过分区查找不同值的数量变得更加复杂。 Oracle 需要知道每个分区和整个 table 的不同值的数量。将少量行添加到单个分区可能会显着改变整个 table 的结果,这就是为什么 Oracle 默认必须 re-scan 整个 table.

比如,想想生日问题。假设有一个用于人群的分区,其中有一个 BIRTHDAY 列。如果分区 A 有 15 个不同的生日,分区 B 有 15 个不同的生日,那么整个 table 有多少个不同的生日?大概不到30.

增量统计通过为每个分区创建概要来​​解决该问题。这些提要可以快速组合在一起以 re-estimate 不同值的全球数量,而无需 re-scan 每个分区。它只需要为每个分区存储一点额外的数据。