Как повысить производительность в Oracle с помощью SELECT DISTINCT

StackOverflow https://stackoverflow.com/questions/1051593

  •  20-08-2019
  •  | 
  •  

Вопрос

В настоящее время я работаю над развертыванием ERP на основе OFBIZ. Используемая база данных является Oracle 10G Enterprise

Одной из самых больших проблем являются некоторые проблемы с производительностью Oracle, при анализе журналов ofbiz, следующий запрос:

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID,
 SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY, 
 FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID, 
 ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID, 
 AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE, 
 REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, 
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM 
ERP.ORDER_HEADER WHERE ((STATUS_ID = :v0 OR STATUS_ID = :v1 OR STATUS_ID = :v2) AND 
(ORDER_TYPE_ID = :v3)) ORDER BY ORDER_DATE DESC

очень медленно.Мы протестировали выполнение запроса без DISTINCT, и это занимает около 30 секунд.В таблице 4.000.000+ регистров.Есть индекс для поля OrderId PK и почти для каждого другого поля.

EXPLAIN PLAN с DISTINCT:

SELECT STATEMENT () (null)
 SORT (ORDER BY)    (null)
  HASH (UNIQUE) (null)
   TABLE ACCESS (FULL)  ORDER_HEADER

и без DISTINCT:

SELECT STATEMENT () (null)
 SORT (ORDER BY)    (null)
  TABLE ACCESS (FULL)   ORDER_HEADER

есть идеи по настройке Oracle для повышения производительности запросов такого типа?Очень сложно переписать запрос, потому что он автоматически генерируется OfBiz, поэтому я думаю, что решение о настройке Oracle

заранее спасибо

РЕДАКТИРОВАТЬ:Я проанализировал запрос с помощью tkprof, как предложили Роб ван Вейк и haffax, и результат следующий:

********************************************************************************

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID,
 SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY, 
 FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID, 
 ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID, 
 AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE, 
 REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, 
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM 
ERP.ORDER_HEADER WHERE STATUS_ID = 'ORDER_COMPLETED' ORDER BY ORDER_DATE DESC

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      9.10     160.81      66729      65203         37          50
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      9.14     160.83      66729      65203         37          50

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 58  

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       1        0.00          0.00
  db file scattered read                       8178        0.28        146.55
  direct path write temp                       2200        0.04          4.22
  direct path read temp                          36        0.14          2.01
  SQL*Net more data to client                     3        0.00          0.00
  SQL*Net message from client                     1        3.36          3.36
********************************************************************************

Кажется, проблема в «рассеянном чтении файла БД», есть идеи, как настроить oracle, чтобы уменьшить время ожидания в этом событии?

Проследите за новым результатом tkprof, на этот раз закрывая сеанс:

********************************************************************************

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID,
 SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY,
 FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID,
 ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID,
 AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE,
 REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP,
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM
ERP.ORDER_HEADER WHERE STATUS_ID = 'ORDER_COMPLETED' ORDER BY ORDER_DATE DESC

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.01          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        1      8.23      47.66      66576      65203         31          50
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      8.26      47.68      66576      65203         31          50

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 58 

Rows     Row Source Operation
-------  ---------------------------------------------------
     50  SORT ORDER BY (cr=65203 pr=66576 pw=75025 time=47666679 us)
3456659   TABLE ACCESS FULL ORDER_HEADER (cr=65203 pr=65188 pw=0 time=20757300 us)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       1        0.00          0.00
  db file scattered read                       8179        0.14         34.96
  direct path write temp                       2230        0.00          3.91
  direct path read temp                          52        0.14          0.84
  SQL*Net more data to client                     3        0.00          0.00
  SQL*Net message from client                     1     1510.62       1510.62
********************************************************************************
Это было полезно?

Решение

Если разница между двумя запросами существенна, это было бы удивительно.Вы упомянули, что запрос без DISTINCT занимает около 30 секунд.Сколько времени занимает запрос с DISTINCT?

Можете ли вы показать выходные данные запроса tkprof с помощью DISTINCT после того, как вы отследили сеанс с помощью «изменить события набора сеансов '10046 контекст имени трассировки навсегда, уровень 8'» и отключиться после завершения запроса?Таким образом, мы можем увидеть, на что на самом деле тратится время и ожидает ли оно чего-то (может быть, температура чтения по прямому пути?)

С уважением, Роб.


Продолжение после публикации файла tkprof:

Я вижу, вам удалось получить выходные данные tkprof, но, к сожалению, вы не отключили сеанс перед созданием файла tkprof.Теперь курсор остался открытым, и ему не удалось записать строки STAT# в ваш файл трассировки.Вот почему в вашем файле tkprof нет операции источника плана/строки.Было бы неплохо, если бы вы могли повторить процесс, если приведенное ниже предложение окажется чушью.

Небольшое предположение с моей стороны:Я думаю, что DISTINCT практически бесполезен, потому что вы выбираете так много столбцов.Если это правда, то ваш предикат «WHERE STATUS_ID = 'ORDER_COMPLETED'» очень избирательен, и вам будет полезно иметь индекс по этому столбцу.После создания индекса убедитесь, что вы проанализировали его правильно, возможно, даже с включенной гистограммой, если значения данных искажены.Конечным результатом будет другой план для этого запроса, начиная со СКАНИРОВАНИЯ ИНДЕКСНОГО ДИАПАЗОНА, за которым следует ДОСТУП К ТАБЛИЦЕ ПО ROWID, что приводит к очень быстрому запросу.

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

exec dbms_stats.gather_table_stats([владелец],[table_name],cascade=>true,method_opt=>'ДЛЯ ВСЕХ ИНДЕКСИРОВАННЫХ СТОЛБЦИЙ РАЗМЕР')

С уважением, Роб.

Другие советы

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

SELECT /*+ index(HEADERS IDX_ORDER_DATE_DESC) */ ... 
FROM ERP.ORDER_HEADER HEADERS
WHERE ...
ORDER BY ORDER_DATE DESC

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

Объявлен ли ORDER_ID как PK с использованием ограничения PRIMARY KEY?Потому что в этом случае я ожидаю, что оптимизатор распознает, что DISTINCT в этом запросе является излишним, и оптимизирует его.Без ограничения он не будет знать, что оно лишнее, и поэтому будет тратить ненужные и значительные усилия на «дедублирование» результатов.

Попробуйте отключить агрегацию хэшей:

select /*+ no_use_hash_aggregation*/ distinct ...

http://oracle-randolf.blogspot.com/2011/01/hash-aggregation.html

При устранении неполадок в приложениях, в которых у меня нет контроля над SQL, я обнаружил, что пакет dbms_sqltune экономит много времени.Видеть http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28419/d_sqltun.htm и да, к сожалению, у вас должна быть лицензия на его использование.

В этом пакете есть процедуры для запуска анализа настройки по определенному sql_id в общем пуле или репозитории AWR.Анализ будет содержать рекомендации по индексированию, если необходимо внести какие-либо улучшения с помощью дополнительных индексов.Что еще более важно, анализатор может обнаружить улучшенный путь доступа, который он может реализовать с помощью того, что Oracle называет профилем SQL — это набор подсказок, которые будут храниться и использоваться всякий раз, когда этот sql_id выполняется.Это происходит без необходимости кодирования подсказок в операторе SQL, а также есть возможность выполнить то, что вы можете назвать нечетким сопоставлением, если ваше приложение генерирует в операторе литеральные значения вместо переменных привязки.

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

Oracle обращается ко всей таблице каждый раз, когда вы запускаете запрос ( TABLE ACCESS (FULL) ).Создание INDEX для столбцов STATUS_ID и ORDER_TYPE_ID

CREATE INDEX ERP.ORDER_HEADER_I1 ON ERP.ORDER_HEADER ( STATUS_ID, ORDER_TYPE_ID );

очень поможет, особенно если в таблице ORDER_HEADER есть несколько разных значений STATUS_ID и ORDER_TYPE_ID.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top