收集 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_STATS
的 GRANULARITY
参数。
默认情况下,会为分区和全局收集统计信息 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 每个分区。它只需要为每个分区存储一点额外的数据。
我有一个 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_STATS
的 GRANULARITY
参数。
默认情况下,会为分区和全局收集统计信息 table。将值更改为 APPROX_GLOBAL AND PARTITION
可能会避免全局 table.
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.
但是通过分区查找不同值的数量变得更加复杂。 Oracle 需要知道每个分区和整个 table 的不同值的数量。将少量行添加到单个分区可能会显着改变整个 table 的结果,这就是为什么 Oracle 默认必须 re-scan 整个 table.
比如,想想生日问题。假设有一个用于人群的分区,其中有一个 BIRTHDAY
列。如果分区 A 有 15 个不同的生日,分区 B 有 15 个不同的生日,那么整个 table 有多少个不同的生日?大概不到30.
增量统计通过为每个分区创建概要来解决该问题。这些提要可以快速组合在一起以 re-estimate 不同值的全球数量,而无需 re-scan 每个分区。它只需要为每个分区存储一点额外的数据。