собирать статистику таблицы на раздел обрабатывает всю таблицу - PullRequest
1 голос
/ 03 июня 2019

У меня есть таблица с 20 разделами. Каждый раздел имеет около 190 миллионов записей. Мне нужно периодически собирать статистику для таблицы во время обработки, которую я делаю с помощью команды DBMS_STATS.GATHER_TABLE_STATS. Когда в таблице был только 1 раздел, на его завершение уходило около 4 минут. Со временем количество разделов выросло, и время, которое требуется для collect_table_stats, также увеличилось. Затем я переключился на сбор статистики только по разделу, добавив параметр PARTNAME в команду GATHER_TABLE_STATS, но время, которое требуется, не уменьшилось. Я даже создал новый раздел, в котором всего 1000 строк, и когда я собираю статистику по этому разделу, это все равно занимает от 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, но ничто не оказывает статистически значимого влияния на время выполнения.

Я только что увидел ответ об использовании инкрементной статистики, и я собираюсь попробовать это, но я хотел бы понять, почему сбор статистики по разделу занимает так много времени, и если я что-то делаю неправильно.

1 Ответ

1 голос
/ 04 июня 2019

Возможно, вы захотите взглянуть на параметр GRANULARITY в DBMS_STATS.GATHER_TABLE_STATS.

По умолчанию статистика собирается как для раздела, так и для глобальной таблицы.Изменение значения на 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 может использовать алгоритм, подобный HyperLogLog , для оценки значений на основе одного прохода таблицы.Вот почему вам нужно сохранить значение ESTIMATE_PERCENT по умолчанию - быстрее сканировать всю таблицу, чем сортировать 10% таблицы.

Но найти число различных значений становится сложнее при разбиении,Oracle должен знать количество различных значений на раздел и для всей таблицы.Добавление небольшого количества строк в один раздел может существенно изменить результаты для всей таблицы, поэтому Oracle по умолчанию должна повторно сканировать всю таблицу.

Например, подумайте о проблеме дня рождения.Представьте, что есть раздел для групп людей с колонкой BIRTHDAY.Если в разделе A 15 различных дней рождения, а в разделе 15 15 различных дней рождения, сколько различных дней рождения у всей таблицы?Вероятно, меньше 30.

Инкрементная статистика решает эту проблему путем создания краткого обзора для каждого раздела.Эти синопсы могут быть быстро объединены вместе, чтобы переоценить общее количество различных значений без повторного сканирования каждого раздела.Требуется лишь хранить немного дополнительных данных для каждого раздела.

...