2015-03-04 8 views
13

Я разрабатываю DWH на Oracle 11g. У нас есть несколько больших таблиц (250+ миллионов строк), разделенных по значению. Каждый раздел назначается другому источнику питания, и каждый раздел не зависит от других, поэтому их можно загружать и обрабатывать одновременно.сбор параллельной статистики по таблице разделенных Oracle 11g

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

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

Таким образом, для каждого параллельного ETL потока, мы

  1. Обрезать раздел
  2. Загрузка данных из промежуточной области с

SELECT /*+ APPEND */ INTO big_table PARTITION(part1) FROM temp_table WHERE partition_colum = PART1

(после этого у нас есть прямой путь и мы не блокировать всю таблицу)

  1. Мы собираем статистику для измененного раздела.

На первом этапе проекта, мы использовали стратегию APPROX_GLOBAL_AND_PARTITION и работал как шарм

dbms_stats.gather_table_stats(ownname=>myschema, 
           tabname=>big_table, 
           partname=>part1, 
           estimate_percent=>1, 
           granularity=>'APPROX_GLOBAL_AND_PARTITION', 
           CASCADE=>dbms_stats.auto_cascade, 
           degree=>dbms_stats.auto_degree) 

Но мы имели тот недостаток, что, когда мы загрузили небольшой раздел, то APPROX_GLOBAL часть была (все еще намного быстрее GLOBAL), а для небольшого раздела у нас было, например, 10 секунд загрузки и 20 минут статистики.

Так мы предложили, чтобы переключиться на INCREMENTAL STATS особенность 11g, что означает, что вы не указали раздел, который вы модифицировали, вы оставляете все параметры в автоматическом, и Oracle делает это волшебство, автоматически понимание какие разделы (разделы) были затронуты. И это действительно работает, мы действительно ускорили небольшой раздел. После включения функции, вызов стал

dbms_stats.gather_table_stats(ownname=>myschema, 
           tabname=>big_table, 
           estimate_percent=>dbms_stats.auto_sample_size, 
           granularity=>'AUTO', 
           CASCADE=>dbms_stats.auto_cascade, 
           degree=>dbms_stats.auto_degree) 

уведомление, что вы не пропустите раздел больше, и вы не указали образец процента.

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

Предположим, что у нас есть 2 больших раздела, которые запускаются в одно и то же время, они закончат фазу загрузки почти одновременно.

  1. Первая нить завершает инструкцию вставки, фиксирует и запускает сбор статистических данных. В процедуре статистики есть два измененных раздела (это правильно, один заполнен, а второй усечен, с текущей транзакцией), корректно обновляет статистику для обоих разделов.

  2. В конце концов второй раздел заканчивается, собирает статистику, он видит, что все разделы уже обновлены, и ничего не делает (это НЕ правильно, потому что второй поток передал данные тем временем).

Результат:

PARTITION NAME | LAST ANALYZED  | NUM ROWS | BLOCKS | SAMPLE SIZE 
----------------------------------------------------------------------- 
PART1   | 04-MAR-2015 15:40:42 | 805731 | 20314 | 805731 
PART2   | 04-MAR-2015 15:41:48 | 0  | 16234 | (null) 

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

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

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

Я что-то не так? Это ошибка Oracle? Как я могу гарантировать, что все статистические данные всегда обновляются с включенной функцией инкрементной статистики и высоким уровнем параллелизма?

+0

Взгляните на 'DBA_TAB_PARTITIONS' и' begin dbms_stats.flush_database_monitoring_info; конец; '. Кажется, что некоторые грязные чтения продолжаются с данными изменения таблицы - несколько сеансов видят друг друга в незанятых вставках, и один сеанс может очистить эти данные для всех сеансов. Я не думаю, что это специфично для инкрементной статистики. Это может помочь вставить и удалить одну фиктивную строку прямо перед вызовом 'DBMS_STATS', чтобы помочь принудительно собрать.Я бы хотел опубликовать больше, но у меня нет достаточно времени для полного ответа прямо сейчас. –

+0

К сожалению, у меня нет привилегий DBA, и я мот даже владелец стола. docs, кстати, говорят, что «поскольку процедуры GATHER _ * _ STATS внутренне скрывают информацию мониторинга, нет необходимости запускать эту процедуру перед сбором статистики». Я пытаюсь собрать только статистику уровня раздела (которая работает) и периодически обновлять глобальную сеть, чтобы обойти проблему, пока я не найду окончательное решение. – Francesco

+0

Вы должны предложить более подробную информацию. Когда начинается точно ваша вставка? до или после появления подозрительной стальной статистики? какой из них заканчивается первым вставкой? hou много времени берет коммиты? сколько времени занимает статистика статистики воровства статистики? Может быть, сбор на первом разделе происходит во время второй сессии? –

ответ

2

мне удалось достичь достойного компромисса с этой функцией.

PROCEDURE gather_tb_partiz(
    p_tblname IN VARCHAR2, 
    p_partname IN VARCHAR2) 
IS 
    v_stale all_tab_statistics.stale_stats%TYPE; 
BEGIN 
    BEGIN 
    SELECT stale_stats 
    INTO v_stale 
    FROM user_tab_statistics 
    WHERE table_name = p_tblname 
    AND object_type = 'TABLE'; 
    EXCEPTION 
    WHEN NO_DATA_FOUND THEN 
    v_stale := 'YES'; 
    END; 
    IF v_stale = 'YES' THEN 
    dbms_stats.gather_table_stats(ownname=>myschema, 
            tabname=> p_tblname, 
            partname=>p_partname, 
            degree=>dbms_stats.auto_degree, 
            granularity=>'APPROX_GLOBAL AND PARTITION') ; 
    ELSE 
    dbms_stats.gather_table_stats(ownname=>myschema, 
           tabname=>p_tblname, 
           partname=>p_partname, 
           degree=>dbms_stats.auto_degree, 
           granularity=>'PARTITION') ; 
    END IF; 
END gather_tb_partiz; 

В конце каждого ETL, если количество добавленных/удаленных/измененных строк достаточно низкой, чтобы не отметить таблицу как несвежий (10% по умолчанию, может быть настроен с параметром STALE_PERCENT), собираю только статистика разделов; в противном случае я собираю глобальную статистику и разделы.

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

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

Я не уверен, что с инкрементным включением «APPROX_GLOBAL AND PARTITION» и «GLOBAL AND PARTITION» чем-то отличаются друг от друга, потому что и инкрементные, и приближенные делают в основном одно и то же: совокупная статистика и гистограммы без полного сканирования ,

-1

Я немного ржавчивый, так что в первую очередь вопрос: Вы пробовали сериализовать загрузку разделов? Если да, то сколько времени и насколько хорошо работает статистика? Обратите внимание: поскольку время загрузки намного меньше, чем сбор статистики, я предполагаю, что это также может действовать как временное обходное решение.

Append намек ли влияет повторить размер, то есть сделка только прослеживает что-то, что статистика не может считаться новые данные: http://oracle-base.com/articles/misc/append-hint.php

Мысли вслух: так как прямая вставка путь делает добавление строки в конце раздела и в конечном итоге обновляет метаданные в конце, уже запущенная статистика сбора потоков могла бы читать не обновленные (устаревшие) данные. Таким образом, это может быть не ошибка, и блокирующие потоки ничего не достигнут.

Вы можете протестировать это поведение, временно переключив свою таблицу/раздел на LOGGING, например, и посмотрите, как это работает (медленнее, конечно, но это тест). Ты можешь сделать это?

EDIT: постепенная статистика должна работать в любом случае, даже отключение параллельного сбора статистики, поскольку она reiles на дополнительных значениях, независимо от того, как они были собраны: https://blogs.oracle.com/optimizer/entry/incremental_statistics_maintenance_what_statistics

+0

I * в некотором роде * попробовал сериализацию загрузки разделов. Я делал (и я был неправ) + APPEND без указания раздела, это заставило всю таблицу заблокировать, так что на самом деле не более одного потока могли одновременно загружать данные в одну и ту же таблицу. Но поскольку у нас есть процесс продолжительностью 16 часов, сериализация (с 10 параллельными потоками), сериализация их не является жизнеспособным способом. У нас довольно жесткие сроки для наших процессов. О LOGGING, для чего я вижу, все таблицы уже находятся в режиме LOGGING, и я никак не могу его изменить. – Francesco

1

Вы пытались инкрементальная статистика, но по-прежнему явно указать раздел для анализа?

dbms_stats.gather_table_stats(ownname=>myschema, 
           tabname=>big_table, 
           partname=>part, 
           degree=>dbms_stats.auto_degree); 
+0

Я пробовал, и, учитывая потраченное время, кажется, что это ведет себя как «ГЛОБАЛЬНОЕ И РАЗРЕШЕНИЕ». Но я снова проверю дважды. – Francesco

+0

Нет, я ошибся. Необязательная степень детализации по умолчанию - AUTO, что переопределяет наличие имени части. Он собирает статистику для всех затронутых разделов, даже если указано имя части, Я просто проверил это. – Francesco

1

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

  • У вас есть отдельный сбор GLOBAL stats, выполненный вашим инструментом ETL сразу после загрузки всех разделов. Если это слишком длительное время, играйте с оценкой_перцента, так как dbms_stats.auto_degree, вероятно, будет больше 1%
  • Собрать глобальную (а также всю старую) статистику в отдельном задании базы данных, которое будет запущено позже в течение дня, после всех данных загружается в DW.

Ключевым моментом является то, что устаревшая статистика, которая незначительно отличается от свежей, почти так же хороша. Если статистика показывает вам 0 строк, они будут убивать любой запрос.

+0

Я принял аналогичное решение. После каждой загрузки ETL я проверяю, являются ли глобальные статистические данные устаревшими, если они есть, я собираю раздел и глобальный, в противном случае - только раздел. Это обеспечивает хорошую производительность, потому что, когда я загружаю небольшие разделы, которые не изменяют более 10% от общего числа строк, нет необходимости также обновлять глобальные. – Francesco

0

Просьба проверить, используете ли вы DBMS_STATS для установки предпочтений таблицы для сбора инкрементной статистики. This oracle blog объясняет, что статистика будет собрана после каждой затронутой строки.

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

BEGIN 
DBMS_STATS.SET_TABLE_PREFS(myschema,'BIG_TABLE','INCREMENTAL','TRUE'); 
END; 
+0

Если статистика запущена и раздел изменен, она не собирает новую статистику, тогда она выглядит как ошибка – psaraj12

+0

Да, я это сделал. Я проверил, что все предварительные условия для инкрементной статистики выполнены. – Francesco

1

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

Однако для этого вам необходимо включить инкрементную функцию для таблицы (11gR1).

EXEC DBMS_STATS.SET_TABLE_PREFS('<Owner>','BIG_TABLE','INCREMENTAL','TRUE');

В конце каждой нагрузки, собирать статистику таблицы с помощью команды GATHER_TABLE_STATS. Вам не нужно указывать имя раздела. Кроме того, не указывайте параметр детализации.

EXEC DBMS_STATS.GATHER_TABLE_STATS('<Owner>','BIG_TABLE');

+0

Я не могу следовать вашему решению. Как я сказал в первоначальном посте, каждый раздел каждой таблицы является независимым от других, и мы имеем высокую степень параллелизма. Как только partitionA загружается в таблицу1, данные начинают обрабатываться и перемещаются в таблицу1 (с тем же самым элементом A). Между тем, мы могли бы получить partitionB, который должен быть загружен и обработан одновременно одним и тем же способом. Я не могу дождаться, что таблица1 будет полностью загружена и проанализирована, я бы потратил слишком много времени. – Francesco

+0

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

 Смежные вопросы

  • Нет связанных вопросов^_^