Как улучшить производительность в Oracle, используя SELECT DISTINCT - PullRequest
2 голосов
/ 27 июня 2009

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

.

Одна из самых больших проблем - некоторые проблемы производительности оракула, анализируя журналы 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+ регистров. Есть индекс для поля PK orderId и почти все остальные поля

ОБЪЯСНИТЕ ПЛАН с 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

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

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

РЕДАКТИРОВАТЬ: я проанализировал запрос с использованием 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      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
********************************************************************************

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

Получите новый результат 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
********************************************************************************

Ответы [ 6 ]

2 голосов
/ 27 июня 2009

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

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

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

1 голос
/ 17 сентября 2012

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

select /*+ no_use_hash_aggregation*/ distinct ...

http://oracle -randolf.blogspot.com / 2011/01 / хэш-aggregation.html

1 голос
/ 28 июня 2009

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

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

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


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

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

Небольшое предположение с моей стороны: я думаю, что DISTINCT почти не работает, потому что вы выбираете так много столбцов. Если это так, то ваш предикат "WHERE STATUS_ID = 'ORDER_COMPLETED'" очень избирателен, и вам будет полезно иметь индекс для этого столбца. После создания индекса убедитесь, что вы правильно его проанализировали, возможно, даже с гистограммой, если значения данных искажены. Конечным результатом будет другой план для этого запроса, начиная с сканирования индекса INDEX RANGE, за которым следует TABLE ACCESS BY ROWID, что приводит к очень быстрому запросу.

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

exec dbms_stats.gather_table_stats ([владелец], [имя_таблицы], каскад => true, method_opt => 'ДЛЯ ВСЕХ ИНДЕКСИРОВАННЫХ РАЗМЕРА КОЛОНН' ')

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

1 голос
/ 27 июня 2009

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

0 голосов
/ 28 июня 2009

При устранении неполадок в приложениях, где у меня нет контроля над 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, и есть также возможность сделать то, что вы можете считать нечетким сопоставлением, если ваше приложение генерирует литеральные значения в выражении вместо переменных связывания.

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

0 голосов
/ 27 июня 2009

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.

...