2016-07-27 3 views
21

У меня есть инструкция SELECT, которая работает очень медленно, это удерживает наш ночной процесс.Оптимизация производительности запроса SELECT

Этот запрос: (Пожалуйста, не комментарий о неявной присоединиться к синтаксису, это автоматически генерируется Informatica, который работает этот код):

SELECT * 
    FROM STG_DIM_CRM_CASES,V_CRM_CASE_ID_EXISTS_IN_DWH,stg_scd_customers_key 
WHERE STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+) 
    AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+) 
    and STG_DIM_CRM_CASES.Case_Create_Date between stg_scd_customers_key.start_date(+) and stg_scd_customers_key.end_date(+) 

редактировать: Реальный запрос выбирает только account_number,start_date,end_date и другой столбец, который не индексируется.

Таблицы информация:

STG_DIM_CRM_CASES

Index - (Account_Number,Case_Create_Date) 
size - 270k records. 

stg_scd_customers_key

Index - Account_Number,Start_Date,End_Date 
Partitioned - End_Date 
Size - 500 million records. 

V_CRM_CASE_ID_EXISTS_IN_DWH (View) -

select t.case_id 
from crm_ps_rc_case t, dim_crm_cases x 
where t.case_id=x.crm_case_id; 

dim_crm_cases -

Indexed - (crm_case_id) 
Size - 100 million . 

crm_ps_rc_case -

Size - 270k records 

Редактировать - Если бы это не было ясно, представление возвращает 270K записей.

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

Вот план выполнения:

6 | 0 | SELECT STATEMENT    |        | 3278K| 1297M| 559K (4)| 02:10:37 |  |  |  |  |   | 
7 | 1 | PX COORDINATOR     |        |  |  |   |   |  |  |  |  |   | 
8 | 2 | PX SEND QC (RANDOM)   | :TQ10003     | 3278K| 1297M| 559K (4)| 02:10:37 |  |  | Q1,03 | P->S | QC (RAND) | 
9 |* 3 | HASH JOIN OUTER    |        | 3278K| 1297M| 559K (4)| 02:10:37 |  |  | Q1,03 | PCWP |   | 
10 | 4 |  PX RECEIVE     |        | 29188 | 10M| 50662 (5)| 00:11:50 |  |  | Q1,03 | PCWP |   | 
11 | 5 |  PX SEND HASH    | :TQ10002     | 29188 | 10M| 50662 (5)| 00:11:50 |  |  | Q1,02 | P->P | HASH  | 
12 |* 6 |  HASH JOIN RIGHT OUTER  |        | 29188 | 10M| 50662 (5)| 00:11:50 |  |  | Q1,02 | PCWP |   | 
13 | 7 |  BUFFER SORT    |        |  |  |   |   |  |  | Q1,02 | PCWC |   | 
14 | 8 |   PX RECEIVE    |        | 29188 | 370K| 50575 (5)| 00:11:49 |  |  | Q1,02 | PCWP |   | 
15 | 9 |   PX SEND BROADCAST  | :TQ10000     | 29188 | 370K| 50575 (5)| 00:11:49 |  |  |  | S->P | BROADCAST | 
16 | 10 |   VIEW     | V_CRM_CASE_ID_EXISTS_IN_DWH | 29188 | 370K| 50575 (5)| 00:11:49 |  |  |  |  |   | 
17 |* 11 |   HASH JOIN   |        | 29188 | 399K| 50575 (5)| 00:11:49 |  |  |  |  |   | 
18 | 12 |    TABLE ACCESS FULL | CRM_PS_RC_CASE    | 29188 | 199K| 570 (1)| 00:00:08 |  |  |  |  |   | 
19 | 13 |    INDEX FAST FULL SCAN| DIM_CRM_CASES$1PK   | 103M| 692M| 48894 (3)| 00:11:25 |  |  |  |  |   | 
20 | 14 |  PX BLOCK ITERATOR  |        | 29188 | 10M| 87 (2)| 00:00:02 |  |  | Q1,02 | PCWC |   | 
21 | 15 |   TABLE ACCESS FULL  | STG_DIM_CRM_CASES   | 29188 | 10M| 87 (2)| 00:00:02 |  |  | Q1,02 | PCWP |   | 
22 | 16 |  BUFFER SORT     |        |  |  |   |   |  |  | Q1,03 | PCWC |   | 
23 | 17 |  PX RECEIVE     |        | 515M| 14G| 507K (3)| 01:58:28 |  |  | Q1,03 | PCWP |   | 
24 | 18 |  PX SEND HASH    | :TQ10001     | 515M| 14G| 507K (3)| 01:58:28 |  |  |  | S->P | HASH  | 
25 | 19 |  PARTITION RANGE ALL  |        | 515M| 14G| 507K (3)| 01:58:28 |  1 | 2982 |  |  |   | 
26 | 20 |   TABLE ACCESS FULL  | STG_SCD_CUSTOMERS_KEY  | 515M| 14G| 507K (3)| 01:58:28 |  1 | 2982 |  |  |   | 
27 ------------------------------------------------------------------------------------------------------------------------------------------------------------ 
28 
29 Predicate Information (identified by operation id): 
30 --------------------------------------------------- 
31 
32  3 - access("STG_DIM_CRM_CASES"."ACCOUNT_NUMBER"="STG_SCD_CUSTOMERS_KEY"."ACCOUNT_NUMBER"(+)) 
33   filter("STG_DIM_CRM_CASES"."CASE_CREATE_DATE">="STG_SCD_CUSTOMERS_KEY"."START_DATE"(+) AND 
34    "STG_DIM_CRM_CASES"."CASE_CREATE_DATE"<="STG_SCD_CUSTOMERS_KEY"."END_DATE"(+)) 
35  6 - access("STG_DIM_CRM_CASES"."CRM_CASE_ID"="V_CRM_CASE_ID_EXISTS_IN_DWH"."CASE_ID"(+)) 
36 11 - access("T"."CASE_ID"="X"."CRM_CASE_ID") 

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

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

Заранее спасибо.

+0

Я бы счел, что ваши лучшие ставки. –

+1

Это полный план? Я не вижу Table_Cases и stg_scd. –

+0

Также я вижу, по крайней мере, одну заблокированную операцию «BUFFER SORT». Было бы полезно, если бы вы опубликовали полный план. Может быть более одного дерева DFO. –

ответ

-1

Я хотел бы создать материализованный вид (с refresh fast on demand) для соединения между Table_cases и stg_scd. Я полагаю, что большая часть работы в соединении - это строки, которые не меняются изо дня в день.

+0

Мои коллеги не очень любят 'MV' .. Таким образом, мы можем пересечь список. – sagi

+0

Очень жаль. Возможно, вы можете показать им документацию Oracle для MV - в частности, часть о «одном из основных применений MV заключается в предварительном вычислении дорогостоящих объединений». Какая-то особая причина, по которой им не нравятся MV? – mathguy

+0

Мы все знакомы здесь с 'MV', и я точно не знаю, почему, я думаю, у них есть свои причины. Я уже пытался убедить их в прошлом, что использование MV может быть полезным, но не повезло там :) – sagi

1

Проблема заключается в сканировании все разделы:

18 | PX SEND HASH | : TQ10001 | 515M | 14G | 507K (3) | 01:58:28 | | | | S-> P | HASH | 25 | 19 | РАЗДЕЛ ДИАПАЗОН ВСЕ |
| 515m | 14G | 507K (3) | 01:58:28 | 1 | 2982 | |
| | 26 | 20 | ТАБЛИЦА ДОСТУПА ПОЛНОСТЬЮ | STG_SCD_CUSTOMERS_KEY | 515m | 14G |

Это происходит потому, что вы используете левое соединение в этой таблице. Можете ли вы выбрать 1 раздел, используя переменную привязки? Что такое ключ раздела? Я не вижу намека на параллельность, но, по вашему мнению, он использует параллель. Есть ли параллельная степень на любом уровне объекта? Можете ли вы удалить параллельный и объясняющий план без параллели, пожалуйста?

+0

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

+0

@sagi Вы можете проверить объекты для параллельной установки следующим выражением: 'select degree, dba_tables. * From dba_tables, где trim (degree) <> '1';'. Также возможно, что параллелизм вынужден на уровне сеанса или автоматизирован на уровне системы. Вы можете переопределить параллелизм с помощью подсказки типа '/ * + no_parallel * /'. –

+0

Но разве эта возможность не ускоряет процесс? Все разделы работают параллельно. @jonheller – sagi

1

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

Общий эффект представления заключается в том, чтобы добавить столбец CASE_ID, который не является нулевым, если в нем найдено CRM_CASE_ID, иначе null. Я заменил представление двумя прямыми объединениями и выражением CASE. Заменяя удобство представления логикой, вы можете напрямую присоединяться к каждой таблице и избегать одного уровня глубины соединения.

Попробуйте запустить эту версию запроса:

SELECT 
    a.*, b.*, c.*, 
    CASE WHEN t.case_id is not null and X.case_id is not null then t.case_id END CASE_ID 
FROM STG_DIM_CRM_CASES a 
LEFT JOIN crm_ps_rc_case t 
    ON t.case_id = a.CRM_CASE_ID 
LEFT JOIN dim_crm_cases x 
    ON x.crm_case_id = a.CRM_CASE_ID 
LEFT JOIN V_CRM_CASE_ID_EXISTS_IN_DWH b 
    ON a.CRM_CASE_ID = b.CASE_ID 
LEFT JOIN stg_scd_customers_key c 
    ON a.account_number = c.account_number 
and a.Case_Create_Date between c.start_date and stg_scd_customers_key.end_date 

Если заменить a.*, b.*, c.* только точные колонки, которые вы на самом деле нужно, вы получите скорость, потому что там просто меньше данных, чтобы вернуться. Если вы также ставите индексы на поисковые ключи и все столбцы, которые вы фактически выбираете (, охватывающий индекс), вы значительно ускорите его, потому что можно использовать только индексный доступ.

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

+0

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

+0

@sagi вид может быть быстрым сам по себе, но может сделать другие части запроса очень медленными, возьмите llok в моем ответе – MtwStark

3

ТЕОРИЯ: То, что я считаю, что случаю это двигатель приходится решать + записи 100х от просмотра присоединиться к 500 млн записей, прежде чем он применяет ограничения критериев (таким образом, он создает перекрестное соединение и даже если он может использовать индексы это много записей для генерации. Затем, хотя вы написали это как внешнее соединение, движок не может обработать его таким образом (я не знаю почему)

Итак, как минимум, 100 м * 500 м = 50 000 м, это много данных для генерации, а затем анализа/ограничения.

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

Области, в которых я хотел бы сосредоточить свое время в устранении неполадок:

  • Исключите вид просто удалить его в качестве потенциального накладных вопроса.
  • Невозможно установить связь между stg_scd_customers_key и V_CRM_CASE_ID_EXISTS_IN_DWH. Это означает, что двигатель может выполнять кросс-соединение до того, как были решены результаты STG_DIM_CRM_CASES на stg_scd_customers_key.

СЧИТАЕТ исключают вид, или с помощью вложенного

Исключая вида:

SELECT * 
    FROM STG_DIM_CRM_CASES 
     ,crm_ps_rc_case t 
     ,dim_crm_cases x 
     ,stg_scd_customers_key 
WHERE t.case_id=x.crm_case_id 
    AND STG_DIM_CRM_CASES.CRM_CASE_ID = t.CASE_ID(+) 
    AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+) 
    AND STG_DIM_CRM_CASES.Case_Create_Date 
     between stg_scd_customers_key.start_date(+) and stg_scd_customers_key.end_date(+) 

используя вложенный:

SELECT * 
    FROM STG_DIM_CRM_CASES 
    (select t.case_id 
    from crm_ps_rc_case t, dim_crm_cases x 
    where t.case_id=x.crm_case_id) V_CRM_CASE_ID_EXISTS_IN_DWH 
     ,stg_scd_customers_key 
WHERE STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+) 
    AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+) 
    AND STG_DIM_CRM_CASES.Case_Create_Date 
     between stg_scd_customers_key.start_date(+) and stg_scd_customers_key.end_date(+) 

Что касается причин: - http://www.dba-oracle.com/art_hints_views.htm

В то время как порядок предложения where НЕ ДОЛЖЕН иметь значение: в выключенном состоянии двигатель работает в указанном порядке, ограничивая 500 м вниз, а затем добавление дополнительных данных из представления логически будет быстрее.

SELECT * 
    FROM STG_DIM_CRM_CASES,stg_scd_customers_key,V_CRM_CASE_ID_EXISTS_IN_DWH 
WHERE STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+) 
    and STG_DIM_CRM_CASES.Case_Create_Date between stg_scd_customers_key.start_date(+) and stg_scd_customers_key.end_date(+) 
    and STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+) 
+0

В плане выполнения не показаны кросс-соединения. –

+0

Я думаю, что я не был достаточно ясен в вопросе, представление возвращает записи 270 тыс., И я считаю, что представление обрабатывается оптимизатором перед внешним запросом. Я попробую, когда приеду на работу , Спасибо за ответ . – sagi

+2

maths: 100m * 500m = 50,000mm = 50,000,000,000m (при условии, что m - миллион) – user5994461

1

Ваша проблема заключается в том, что Oracle действительно имеет только два пути, чтобы получить строки, необходимые от stg_scd_customers_key. Либо (A) он делает один FULL SCAN этой таблицы, а затем отфильтровывает строки, которые он не хочет, или (B) он делает 270 000 запросов индекса, от 3 до, возможно, 5 логических операций ввода-вывода каждый (в зависимости от высоты ваш индекс), плюс еще один логический ввод-вывод для фактического чтения блока из таблицы.

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

Что вам нужно сделать, это предоставить Oracle лучший вариант.

Если вы не можете использовать материализованные виды, где вы находитесь, материализованный вид хорошего «бедняка» является тем, что называется индексом покрытия. Теперь это не подходит для вашего запроса, так как вы делаете SELECT *. Но вам действительно нужен каждый столбец от stg_scd_customers_key?

Если вы можете урезать список столбцов, которые вы получаете от stg_scd_customers_key, вы можете создать индекс, который (A) начинается с account_number, start_date и end_date и (В) включает в себя все остальные столбцы, которые нужно выбрать.

Например:

SELECT stg_im_crm_cases.*, V_CRM_CASE_ID_EXISTS_IN_DWH.*, stg_scd_customers_key.column_1, stg_scd_customers_key.column_2 
    FROM STG_DIM_CRM_CASES,V_CRM_CASE_ID_EXISTS_IN_DWH,stg_scd_customers_key 
WHERE STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+) 
    AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+) 
    and STG_DIM_CRM_CASES.Case_Create_Date between stg_scd_customers_key.start_date(+) and stg_scd_customers_key.end_date(+) 

Если вы могли бы сделать, что ваш запрос и создать индекс на stg_scd_customers_key (ACCOUNT_NUMBER, датой_начала, датой_окончания, column_1, column_2), то вам будет дано Oracle лучшей альтернативой. Теперь он может читать только индекс, а не таблицу.

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

+0

Благодарим вас за ответ. Я прокомментировал выбор столбцов в комментариях, я забыл его исправить в вопросе. Я фактически выбираю только нужные столбцы (account_number, start_date, end_date), которые индексируются, и еще один столбец, который не является. Я не могу добавить индексы в эту конкретную таблицу, поскольку он используется где-то еще с огромными вставками. – sagi

+0

Вариант C: быстрый полный индексный сканирование индекса, охватывающего Account_Number, Start_Date и End_Date. ? –

1

некоторые соображения:

1) INDEX
crm_ps_rc_case не имеет индекс case_id это проблема, вы соединяющую 270k < -> 100м с HASH JOIN (не хорошо)

2) ВЫБРАННЫЕ КОЛОННЫ
вид V_CRM_CASE_ID_EXISTS_IN_DWH выбирает t.case_id, но он должен выбрать x.crm_case_id вместо этого, по крайней мере, пока вы не решите индексация t.case_id. Это будет распространяться HASH JOIN по всему вашему плану выполнения .. (не хорошо)

3) МЕЖДУ
диапазон присоединения/фильтрации всегда проблема, особенно на больших таблицах, но вы могли бы ограничить эту проблему, добавив условия на интервале , позвольте мне объяснить, попробуйте добавить эти условия к вашей статье WHERE:

AND stg_scd_customers_key.end_date = (
     SELECT min(r.end_date) 
     FROM stg_scd_customers_key r 
     WHERE r.end_date >= STG_DIM_CRM_CASES.Case_Create_Date 
) 
AND stg_scd_customers_key.start_date = (
     SELECT max(r.start_date) 
     FROM stg_scd_customers_key r 
     WHERE r.start_date <= STG_DIM_CRM_CASES.Case_Create_Date 
) 

да, он будет рассчитывать 270K * 2 подзапросы, но окончательное объединение будет работать на гораздо меньше РИК предельных операций ввода-вывода (она должна быть лучше)

4) ИНДЕКС COLUMN ORDER
есть противоречивые сообщения, если это произойдет, или если это не имеет значения, но в моем опыте .. это делает. Это может быть только незначительное улучшение, но вы можете попытаться изменить индекс на stg_scd_customers_key, инвертируя порядок Start_Date и End_Date. По моему опыту, я нашел более эффективным для фильтрации диапазона, чтобы иметь верхнюю границу перед нижней границей индекса ,