2017-01-01 2 views
1

У меня есть таблица ABC интервальная перегородка на каждый день. Каждый раздел используется в запросах за этот конкретный день. Даже если я планирую задание @nyt каждый день собирать статистику, тогда запросы, которые используют эту таблицу до сбора статистики, не будут использовать оптимальный план.Соберите статистику по таблице разделов

+0

Добавить план запроса и ddl –

+0

Я хочу знать, когда будет добавлен новый раздел, и есть объемная загрузка данных. У нас есть статистика, собранная автоматически или нет. Если нет, то как мы можем собрать статистику s – oracle

+0

Посмотрите на пакет DBMS_STATS.GATHER_TABLE_STATISTICS, там вы также можете указать раздел. –

ответ

0

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

Чтобы продемонстрировать это, предположим, что у нас нет ежедневной схемы, а в ежегодных разделах данных транзакций. Вопрос в том, было бы нормально собирать статистику, говорят 1 января (или 1 июня или 31. декабря)? Ответ окончательно NO, так как в первом случае раздел будет считаться (почти) пустым, в последнем случае статистика будет реалистичной, но они были собраны слишком поздно.

Имея это в виду, что есть ИМО три возможных подхода к себе это

1) не собирают статистические данные на всех (и использовать динамический отбор проб)

2) собирать статистику разделов повторно (скажем каждый час)

3) не собирают статистику, но установить их так, что запросы выполнения тонкой

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

Примеры данных

Позволяет создать таблицу с одним полным и один почти пустой ежедневных перегородками.

Таблица имеет локальный индекс на столбце GROUP_ID. Целью упражнения является получение FULL TABLE SCAN при доступе к небольшому разделу и INDEX ACCESS при доступе к большому разделу.

CREATE TABLE mytab 
    ( id number not null, 
     group_id number, 
     trans_date date, 
     pad varchar2(4000)) 
PARTITION BY RANGE (trans_date) 
INTERVAL (NUMTODSINTERVAL(1,'DAY')) 
(
    PARTITION part_01 values LESS THAN (TO_DATE('31-12-2016','DD-MM-YYYY')) 
); 

create index mytab_idx1 on mytab(id) local; 
create index mytab_idx2 on mytab(group_id) local; 

-- full day partition 
insert into mytab (id, group_id, trans_date, pad) 
select rownum id, trunc(rownum/1000) group_id, to_date('31122016','ddmmyyyy'), lpad('x',3000,'x') from dual 
connect by level <= 100000; 
commit; 

-- nearly empty day partition 
insert into mytab (id, group_id, trans_date, pad) 
select rownum id, trunc(rownum/1000) group_id, to_date('01012017','ddmmyyyy'), lpad('x',3000,'x') from dual 
connect by level <= 1000; 
commit; 

Dynamic Sampling

Если целевой объект нет статистики на всех, Oracle выполняет динамическую выборку (ака dynamic statistics) С небольшими затратами Oracle вычисляет статистику во время разбора заявления. Так что это не может быть устаревшим.

Доступа к почти пустому разделу Oracle правильного выбор FULL TABLE SCAN

EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR 
select * from mytab 
where trans_date = TO_DATE('01-01-2017','DD-MM-YYYY') and group_id = 0; 

SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL')); 

Plan hash value: 4018216072 

------------------------------------------------------------------------------------------------ 
| Id | Operation    | Name | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
------------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT  |  | 958 | 1905K| 274 (0)| 00:00:01 |  |  | 
| 1 | PARTITION RANGE SINGLE|  | 958 | 1905K| 274 (0)| 00:00:01 |  3 |  3 | 
|* 2 | TABLE ACCESS FULL | MYTAB | 958 | 1905K| 274 (0)| 00:00:01 |  3 |  3 | 
------------------------------------------------------------------------------------------------ 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("TRANS_DATE"=TO_DATE(' 2017-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') 
       AND "GROUP_ID"=0) 

Note 
----- 
    - dynamic statistics used: dynamic sampling (level=2) 

... в то время как доступ к полному разделу INDEX ACCESS используется

EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR 
select * from mytab 
where trans_date = TO_DATE('31-12-2016','DD-MM-YYYY') and group_id = 0; 

SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL')); 

Plan hash value: 984912596 

------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation         | Name  | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
------------------------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT       |   | 1608 | 3198K| 9021 (1)| 00:00:01 |  |  | 
| 1 | PARTITION RANGE SINGLE     |   | 1608 | 3198K| 9021 (1)| 00:00:01 |  2 |  2 | 
|* 2 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| MYTAB  | 1608 | 3198K| 9021 (1)| 00:00:01 |  2 |  2 | 
|* 3 | INDEX RANGE SCAN      | MYTAB_IDX2 | 1608 |  | 2880 (1)| 00:00:01 |  2 |  2 | 
------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("TRANS_DATE"=TO_DATE(' 2016-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 
    3 - access("GROUP_ID"=0) 

Note 
----- 
    - dynamic statistics used: dynamic sampling (level=2) 

Итак, мы видим, что динамическая выборка работает отлично, выбирая надлежащий метод доступа.

Собрать перегородки Статистика Часто

Повторяя работу сбора смягчает проблему, что раздел постоянно растет.

Период зависит от степени трансакции.

Пример сбора статистики для одного раздела только

exec dbms_stats.gather_table_stats(OWNNAME=>user,TABNAME=>'MYTAB', PARTNAME=>'SYS_P10030', CASCADE=> TRUE); 

худшем случае, что следует избегать, что * статистика указывает, что раздел пуст, но (в то же время) раздела тяжело заселены.

Set Статистика

Этот подход предполагает, что «правильный» путь доступа для запросов известно. В нашем примере мы можем получить доступ к почти пустому разделу с FULL TABLE SCAN, но доступ к индексу подходит для такого раздела . Таким образом, мы можем установить статистику разделов так, чтобы всегда выполнялся УКАЗАТЕЛЬ ДОСТУПА.

Одной из возможных (очень простых) схем является копирование статистики за предыдущий день. копии

Этого вызова статистика из раздела SYS_P10029 разметить SYS_P10030

exec DBMS_STATS.COPY_TABLE_STATS (OWNNAME=>user,TABNAME=>'MYTAB',srcpartname=>'SYS_P10029',dstpartname=> 'SYS_P10030');  

Таким образом, в другом слове, сразу после создания раздела статистики инициируется, как для полного заполненного раздела.

0

В моем приложении я запускаю эту процедуру один раз в день по заданию планировщика. Он собирает статистику для самого последнего раздела.

PROCEDURE GatherIndexStats IS 

    CURSOR IndexPartition(indName IN VARCHAR2) IS 
    SELECT INDEX_NAME, PARTITION_NAME 
    FROM USER_IND_STATISTICS i 
     JOIN USER_TAB_PARTITIONS t USING (TABLE_NAME, PARTITION_NAME) 
    WHERE TABLE_NAME = 'ABC' 
     AND i.LAST_ANALYZED IS NULL 
     AND OBJECT_TYPE = 'PARTITION' 
     AND INDEX_NAME = indName 
    ORDER BY INDEX_NAME, PARTITION_NAME DESC 
    OFFSET 1 ROW FETCH FIRST 2 ROW ONLY; 

BEGIN 

    FOR aIndex IN (SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME = 'ABC') LOOP 
     FOR aInd IN IndexPartition(aIndex.INDEX_NAME) LOOP 
      DBMS_STATS.GATHER_INDEX_STATS(USER, aInd.INDEX_NAME, aInd.PARTITION_NAME); 
     END LOOP; 
    END LOOP; 

END GatherIndexStats; 

В моем приложении мне нужно получить статистику индекса, а не полную статистику таблицы. В случае, если вы хотите, чтобы получить индекс и таблиц статистики, процедура использования ниже:

PROCEDURE GatherTableStats IS 

    CURSOR TablePartition IS 
    SELECT INDEX_NAME, PARTITION_NAME 
    FROM USER_TAB_STATISTICS i 
     JOIN USER_TAB_PARTITIONS t USING (TABLE_NAME, PARTITION_NAME) 
    WHERE TABLE_NAME = 'ABC' 
     AND i.LAST_ANALYZED IS NULL 
     AND OBJECT_TYPE = 'PARTITION' 
    ORDER BY PARTITION_NAME DESC 
    OFFSET 1 ROW FETCH FIRST 2 ROW ONLY; 

BEGIN 

    FOR aPart IN TablePartition LOOP 
     DBMS_STATS.GATHER_TABLE_STATS(USER, 'ABC', aPart.PARTITION_NAME); 
    END LOOP; 

END GatherTableStats; 
0

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

Сбор статистики только в ночных работах имеет множество потенциальных недостатков:

  1. Обработка имеет странную зависимость от времени. Окна статистики могут быть сложными для координации. И иногда, если есть слишком много работы, таблица, о которой вы заботитесь, может не успевать проанализироваться.
  2. Существует несколько типов заданий статистики (задания планировщика, DBA_JOBS, auto_tasks), все из которых имеют тенденцию к отключению больше, чем нужно.
  3. Сбор статистики в неподходящее время: намного хуже, чем не имеет статистики вообще. Если нет статистики, то Oracle может использовать динамическую выборку для выполнения достойной работы. Но если только ночная работа просто собирает статистику в течение краткого периода, когда таблица пуста, статистика может быть ужасно неправильной, и производительность будет страдать.Я видел, как это случалось много раз; эти ошибки, как правило, обвиняются в «различиях в окружающей среде», но если вы оставите критический шаг до случайности, то среды будут случайным образом терпеть неудачу.

Сбор статистики как части процесса загрузки данных имеет много потенциальных преимуществ. Так как вы понимаете, процесс и таблиц лучше, чем какая-то общая ночные статистика работы вы можете воспользоваться большим количеством дополнительных функций:

  1. Если система не занята после загрузки данных, то параллелизм можно использовать с параметром, как СТЕПЕНЬ => 8.
  2. Если это прямая запись в 12c, вы можете автоматически собирать статистику при загрузке данных с помощью подсказки GATHER_OPTIMIZER_STATISTICS.
  3. Если это секционированная таблица с интервалом, вы можете захотеть настроить сбор инкрементной статистики. Это позволяет процессу тратить время на сбор статистических данных для раздела, а глобальная статистика обновляется бесплатно.
  4. Если процесс отключен и перестраивает индексы, он может избежать повторной сбор статистики индекса с помощью параметра NOCASCADE => TRUE.

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