2013-08-08 2 views
3

Итак, я навел на это свой мозг и, по общему признанию, не очень хорошо разбираюсь в Oracle. У нас есть таблица, на которой хранится около 60 миллионов записей со значениями, хранящимися в ней для зданий. Добавили соответствующие индексы, которые, по моему мнению, были пригодны, но все же плохие показатели. Вот запрос, который должен помочь:Oracle - оптимизируйте запрос, таблицу с большими базами данных, поле CLOB

SELECT count(*) 
    FROM viewBuildings 
    INNER JOIN tblValues 
      ON viewBuildings.bldg_id = tblValues.bldg_id 
    WHERE bldg_deleted = 0 
    AND (bldg_summary = 1 
     OR (bldg_root = 0 AND bldg_def = 0) 
     OR bldg_parent = 1) 
    AND field_id IN (207) 
    AND UPPER(dbms_lob.substr(v_value, 2000, 1)) = UPPER('2320') 

Таким образом, приведенное выше является лишь одним примером запроса, который может быть построен. Он ищет в tblValues ​​в поле CLOB v_value для соответствия «2320». Он охватывает верхние строчки, поскольку он может искать как числовые, так и текстовые значения. tblValues ​​имеет 60 миллионов записей. Он индексируется идентификатором здания, а также идентификатором поля.

Возможно, мне нужно будет предоставить дополнительную информацию, но, насколько статистика идет, число, которое выскочило мне, было «последовательно получает». Согласованный get = 74069. Это большое количество?

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

EDIT (больше информации): tblBuildings является частью viewBuildings (вид), имеет 80000 записей tblValues ​​имеет значения каждого здания, имеет 68000000 записей tblValues ​​имеет около 550 полей в здании (field_id)

Желаемый результат: Запрос, чтобы вернуть результаты в < 5 секунд. Это необоснованно? Иногда это будет бесконечно работать, иногда может быть 80 секунд.

Объяснить план результаты

Plan hash value: 1480138519 
----------------------------------------------------------------------------------------------------------------------- 
| Id | Operation       | Name        | Rows | Bytes | Cost (%CPU)| Time  | 
-----------------------------------------------------------------------------------------------------------------------| 
| 0 | SELECT STATEMENT     |         |  1 | 192 | 32 (0)| 00:00:01 | 
| 1 | SORT AGGREGATE      |         |  1 | 192 |   |   | 
| 2 | NESTED LOOPS      |         |  1 | 192 | 15 (0)| 00:00:01 | 
| 3 | NESTED LOOPS      |         |  1 | 183 | 12 (0)| 00:00:01 | 
|* 4 |  FILTER       |         |  |  |   |   | 
| 5 |  NESTED LOOPS OUTER    |         |  1 | 64 | 10 (0)| 00:00:01 | 
|* 6 |  TABLE ACCESS BY INDEX ROWID | TBLBUILDINGS      |  1 | 60 |  9 (0)| 00:00:01 | 
|* 7 |  INDEX RANGE SCAN    | SAA_4       | 17 |  |  3 (0)| 00:00:01 | 
| 8 |   NESTED LOOPS    |         |  1 | 21 |  3 (0)| 00:00:01 | 
| 9 |   TABLE ACCESS BY INDEX ROWID| TBLBUILDINGSTATUSES    |  1 | 15 |  2 (0)| 00:00:01 | 
|* 10 |   INDEX RANGE SCAN   | IDX_BUILDINGSTATUS_EXCLUDEQUERY |  1 |  |  1 (0)| 00:00:01 | 
|* 11 |   INDEX RANGE SCAN   | IDX_BUILDING_STATUS_ASID_DELETED |  1 |  6 |  1 (0)| 00:00:01 | 
| 12 |  TABLE ACCESS BY INDEX ROWID | TBLBUILDINGSTATUSES    |  1 |  4 |  1 (0)| 00:00:01 | 
|* 13 |  INDEX UNIQUE SCAN   | PK_TBLBUILDINGSTATUS    |  1 |  |  0 (0)| 00:00:01 | 
|* 14 |  TABLE ACCESS BY INDEX ROWID  | TBLVALUES      |  1 | 119 |  2 (0)| 00:00:01 | 
|* 15 |  INDEX UNIQUE SCAN    | PK_SAA_6       |  1 |  |  1 (0)| 00:00:01 | 
| 16 | INLIST ITERATOR     |         |  |  |   |   | 
|* 17 |  INDEX RANGE SCAN    | SAA_7       |  1 |  9 |  3 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 

    4 - filter("TBLBUILDINGSTATUSES"."BUILDING_STATUS_HIDE_REPORTS" IS NULL OR 
       "TBLBUILDINGSTATUSES"."BUILDING_STATUS_HIDE_REPORTS"=0) 
    6 - filter("TBLBUILDINGS"."BLDG_SUMMARY"=1 OR "TBLBUILDINGS"."BLDG_SUB_BUILDING_PARENT"=1 OR 
       "TBLBUILDINGS"."BLDG_BUILDING_DEF"=0 AND "TBLBUILDINGS"."BLDG_ROOT"=0) 
    7 - access("TBLBUILDINGS"."BLDG_DELETED"=0) 
     filter(NOT EXISTS (SELECT 0 FROM "TBLBUILDINGSTATUSES" "TBLBUILDINGSTATUSES","TBLBUILDINGS" "TBLBUILDINGS" WHERE 
       "TBLBUILDINGS"."BLDG_ID"=:B1 AND "TBLBUILDINGSTATUSES"."BUILDING_STATUS_ID"="TBLBUILDINGS"."BUILDING_STATUS_ID" AND 
       "TBLBUILDINGSTATUSES"."BUILDING_STATUS_EXCLUDE_QUERY"=1)) 
    10 - access("TBLBUILDINGSTATUSES"."BUILDING_STATUS_EXCLUDE_QUERY"=1) 
    11 - access("TBLBUILDINGS"."BLDG_ID"=:B1 AND "TBLBUILDINGSTATUSES"."BUILDING_STATUS_ID"="TBLBUILDINGS"."BUILDING_STATUS_ID") 
     filter("TBLBUILDINGSTATUSES"."BUILDING_STATUS_ID"="TBLBUILDINGS"."BUILDING_STATUS_ID") 
    13 - access("TBLBUILDINGSTATUSES"."BUILDING_STATUS_ID"(+)="TBLBUILDINGS"."BUILDING_STATUS_ID") 
    14 - filter(UPPER("DBMS_LOB"."SUBSTR"("TBLVALUES"."V_VALUE",2000,1))=U'2320') 
    15 - access("TBLVALUES"."FE_ID"=207 AND "TBLBUILDINGS"."BLDG_ID"="TBLVALUES"."BLDG_ID") 
    17 - access("TBLINSPECTORBUILDINGMAP"."IN_ID"=1 AND ("TBLINSPECTORBUILDINGMAP"."IAM_BUILDING_ACCESS_LEVEL"=0 OR 
       "TBLINSPECTORBUILDINGMAP"."IAM_BUILDING_ACCESS_LEVEL"=1) AND "TBLBUILDINGS"."BLDG_ID"="TBLINSPECTORBUILDINGMAP"."BLDG_ID") 

44 rows selected 

Plan hash value: 2137789089 

--------------------------------------------------------------------------------------------- 
| Id | Operation       | Name | Rows | Bytes | Cost (%CPU)| Time  | 
--------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT     |   | 8168 | 16336 | 29 (0)| 00:00:01 | 
| 1 | COLLECTION ITERATOR PICKLER FETCH| DISPLAY | 8168 | 16336 | 29 (0)| 00:00:01 | 
--------------------------------------------------------------------------------------------- 

Хорошо, я собрал статистику, как Вы предложили, а затем вот plan_table_output. Похоже, проблема IDX_CURVAL_FE_ID здесь? Это индекс в таблице значений для идентификатора поля.

SQL_ID d4aq8nsr1p6uw, child number 0 
------------------------------------- 
SELECT /*+ gather_plan_statistics */ count(*)  FROM 
viewAssetsForUser1 INNER JOIN tblCurrentValues   ON 
viewAssetsForUser1.as_id = tblCurrentValues.as_id WHERE as_deleted = 
:"SYS_B_0"  AND (as_summary = :"SYS_B_1"   OR (as_root = 
:"SYS_B_2" AND as_asset_def = :"SYS_B_3")   OR 
as_sub_asset_parent = :"SYS_B_4")  AND fe_id IN (:"SYS_B_5")  
AND UPPER(dbms_lob.substr(cv_value, :"SYS_B_6", :"SYS_B_7")) = 
UPPER(:"SYS_B_8") 

Plan hash value: 4033422776 

----------------------------------------------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation        | Name      | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem | 
----------------------------------------------------------------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT      |       |  1 |  |  1 |00:08:43.19 | 56589 | 56084 |  |  |   | 
| 1 | SORT AGGREGATE      |       |  1 |  1 |  1 |00:08:43.19 | 56589 | 56084 |  |  |   | 
|* 2 | FILTER        |       |  1 |  |  0 |00:08:43.19 | 56589 | 56084 |  |  |   | 
| 3 | NESTED LOOPS      |       |  1 |  |  0 |00:08:43.19 | 56589 | 56084 |  |  |   | 
| 4 |  NESTED LOOPS      |       |  1 | 115 |  0 |00:08:43.19 | 56589 | 56084 |  |  |   | 
|* 5 |  FILTER       |       |  1 |  |  0 |00:08:43.19 | 56589 | 56084 |  |  |   | 
|* 6 |  HASH JOIN RIGHT OUTER   |       |  1 |  82 |  0 |00:08:43.19 | 56589 | 56084 | 1348K| 1348K| 742K (0)| 
| 7 |  TABLE ACCESS FULL    | TBLASSETSTATUSES   |  1 |  4 |  4 |00:00:00.01 |  3 |  0 |  |  |   | 
| 8 |  NESTED LOOPS     |       |  1 |  |  0 |00:08:43.19 | 56586 | 56084 |  |  |   | 
| 9 |   NESTED LOOPS     |       |  1 | 163 |  0 |00:08:43.19 | 56586 | 56084 |  |  |   | 
|* 10 |   TABLE ACCESS BY INDEX ROWID | TBLCURRENTVALUES   |  1 | 163 |  0 |00:08:43.19 | 56586 | 56084 |  |  |   | 
|* 11 |   INDEX RANGE SCAN   | IDX_CURVAL_FE_ID   |  1 | 16283 | 61357 |00:00:05.98 |  132 | 132 |  |  |   | 
|* 12 |   INDEX RANGE SCAN    | SAA_1      |  0 |  1 |  0 |00:00:00.01 |  0 |  0 |  |  |   | 
|* 13 |   TABLE ACCESS BY INDEX ROWID | TBLASSETS     |  0 |  1 |  0 |00:00:00.01 |  0 |  0 |  |  |   | 
|* 14 |  INDEX UNIQUE SCAN    | PK_TBLINSPECTORBRIDGEMAP2 |  0 |  1 |  0 |00:00:00.01 |  0 |  0 |  |  |   | 
|* 15 |  TABLE ACCESS BY GLOBAL INDEX ROWID| TBLINSPECTORASSETMAP  |  0 |  1 |  0 |00:00:00.01 |  0 |  0 |  |  |   | 
----------------------------------------------------------------------------------------------------------------------------------------------------------------- 

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

    2 - filter(:SYS_B_0=0) 
    5 - filter(("TBLASSETSTATUSES"."ASSET_STATUS_HIDE_REPORTS" IS NULL OR "TBLASSETSTATUSES"."ASSET_STATUS_HIDE_REPORTS"=0)) 
    6 - access("TBLASSETSTATUSES"."ASSET_STATUS_ID"="TBLASSETS"."ASSET_STATUS_ID") 
    10 - filter(UPPER("DBMS_LOB"."SUBSTR"("TBLCURRENTVALUES"."CV_VALUE",:SYS_B_6,:SYS_B_7))=SYS_OP_C2C(UPPER(:SYS_B_8))) 
    11 - access("TBLCURRENTVALUES"."FE_ID"=:SYS_B_5) 
    12 - access("TBLASSETS"."AS_DELETED"=:SYS_B_0 AND "TBLASSETS"."AS_ID"="TBLCURRENTVALUES"."AS_ID") 
    13 - filter((("TBLASSETS"."AS_ROOT"=:SYS_B_2 AND "TBLASSETS"."AS_ASSET_DEF"=:SYS_B_3) OR "TBLASSETS"."AS_SUMMARY"=:SYS_B_1 OR 
       "TBLASSETS"."AS_SUB_ASSET_PARENT"=:SYS_B_4)) 
    14 - access("TBLASSETS"."AS_ID"="TBLINSPECTORASSETMAP"."AS_ID" AND "TBLINSPECTORASSETMAP"."IN_ID"=1) 
    15 - filter(("TBLINSPECTORASSETMAP"."IAM_ASSET_ACCESS_LEVEL"=0 OR "TBLINSPECTORASSETMAP"."IAM_ASSET_ACCESS_LEVEL"=1)) 
+1

74069 последовательный получение означает, что запрос, вероятно, читает около 578 МБ данных. Но это не говорит нам о многом. Номер может быть слишком высоким или слишком низким. Во-первых, каковы ваши ожидания в отношении этого запроса? Возвращает ли это небольшое количество строк, которые вы хотите отображать почти мгновенно, но вместо этого требуется X секунд? Также нам нужно увидеть план объяснения. Опубликуйте результаты: 'объясните план для [вашего запроса];' и затем 'select * из таблицы (dbms_xplan.display);'. –

+0

Он может возвращать от 0 до 17 000 строк в зависимости от того, как пользователь пишет запрос. Я надеюсь, что мы сможем достичь этого результата за 5 секунд, но не знаем, реалистично ли это с учетом размера. Но может быть 68 000 000 строк, но вы ищете только в заданной области, чтобы результаты были более сузились. Я обновил оригинальное сообщение с результатами плана объяснения. Дайте мне знать, если вам нужно больше. – hammer

+0

Спасибо за обновление вопроса. Оценки очень малой мощности, Rows = 1. До того, как вы потратите слишком много времени на запрос, вы можете просто повторно собрать статистику, чтобы оптимизатор имел самую свежую информацию. Например: 'begin \t dbms_stats.gather_stats (, 'TBLBUILDINGS'); \t dbms_stats.gather_stats (, 'TBLBUILDINGSTATUSES'); \t dbms_stats.gather_stats (, 'TBLVALUES'); конец; /'. –

ответ

1

Bad Индекс стоимости Если статистика свежи, и оптимизатор имеет относительно хорошую оценку числа элементов, то почему бы это выбрать плохой план? Возможно, есть параметр, делающий индексы выглядят искусственно дешевыми. Посмотрите: select * from v$parameter where name in ('optimizer_index_cost_adj', 'optimizer_index_caching'); Значительно отличаются ли они от значений по умолчанию, 100 и 0?

Также обратите внимание на select * from sys.aux_stats$; Возможно, ваша системная статистика сделает полное сканирование таблицы слишком дорогостоящим. В некоторых версиях Oracle есть ошибки с статистикой рабочей нагрузки, где числа ошибочны на несколько порядков.

Или, может быть, ваш стол просто невероятно огромен, а индекс чтения 16K - лучший путь доступа. Посмотрите на DBA_SEGMENTS.BYTES, чтобы найти размер вашей таблицы и сегмента LOB.

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

LOB STORAGE Учитывая ваш пример, я полагаю, что большинство CLOB относительно невелики? Возможно, у вас есть необычная настройка больших объектов, которая тратит много места, например DISABLE STORAGE IN ROW.Вы можете проверить свою таблицу DDL или опубликовать все ее здесь. Или, если вы можете заменить CLOB на VARCHAR2, это будет еще лучше.

FBI Функциональный индекс на CLOB может значительно ускорить процесс. Но это может быть очень большой индекс: create index TBLCURRENTVALUES_FBI on TBLCURRENTVALUES(UPPER(dbms_lob.substr(v_value, 2000, 1)));

CURSOR_SHARING Запросы меняются немного, что делает настройку трудно. Похоже, эта последняя версия имеет CURSOR_SHARING=FORCE, что необычно. Для дорогостоящего запроса использование литералов может быть хорошим - дополнительное время, потраченное на построение планов запросов, вероятно, стоит того. Если системный параметр не может измениться, просмотрите подсказку /*+ cursor_sharing_exact */.

+0

Спасибо за вашу помощь. Окончательное решение оказалось конвертированием этого CLOB в столбец VARCHAR2. Затем добавьте функциональный индекс в UPPER (v_value), поскольку все наши запросы используют это. Затем последний фрагмент создавал хэш-раздел для идентификатора поля, поэтому в предложениях LIKE было бы меньше данных для сканирования. – hammer

2

Вы можете сделать любое количество оптимизаций, но в конечном итоге это огромный объем данных, которые вызывают проблему. Когда вы выполняете запрос и отслеживаете его на графике производительности на OEM, вы будете потратить значительное количество времени на IO. Это приводит к вводу и выходу данных из памяти.

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

Вы можете прочитать больше об этом по следующему адресу: http://docs.oracle.com/cd/E11882_01/server.112/e25523/partition.htm#g471747

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

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

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

+0

Разделение на такой столбец с высокой производительностью (building/field_id) может создавать определенные проблемы. Разделение INTERVAL может быть очень быстрым. Но создание многих тысяч разделов и сегментов может вызвать проблемы или, по крайней мере, тратить много места. В качестве альтернативы можно использовать более грубую схему разделов, такую ​​как HASH с 128 разделами. Но это все равно будет зависеть от оптимизатора, выбирающего полное сканирование таблицы вместо неэффективного индекса. Местный индекс будет меньше и быстрее, но здесь не на порядок быстрее. –

+0

Благодарим вас за помощь. Подробнее см. Ниже, но я использовал комбинацию вашего ответа с jonearles. Мы получаем довольно хорошие скорости, теперь запрашивая данные. – hammer

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