Заставить Oracle 9i использовать индексы - PullRequest
2 голосов
/ 19 мая 2011

У меня проблема с производительностью при развертывании приложения, разработанного на 10g XE, на клиентском сервере 9i. Один и тот же запрос создает совершенно разные планы запросов в зависимости от сервера:

SELECT DISTINCT FOO.FOO_ID               AS C0,
    GEE.GEE_CODE                         AS C1,
    TO_CHAR(FOO.SOME_DATE, 'DD/MM/YYYY') AS C2,
    TMP_FOO.SORT_ORDER                   AS SORT_ORDER_
FROM TMP_FOO
INNER JOIN FOO ON TMP_FOO.FOO_ID=FOO.FOO_ID
LEFT JOIN BAR ON FOO.FOO_ID=BAR.FOO_ID
LEFT JOIN GEE ON FOO.GEE_ID=GEE.GEE_ID
ORDER BY SORT_ORDER_;

Oracle Database 10g Express Edition, выпуск 10.2.0.1.0 - Производство:

-------------------------------------------------------------------------------------------
| Id  | Operation                       | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |         |     1 |    67 |    10  (30)| 00:00:01 |
|   1 |  SORT UNIQUE                    |         |     1 |    67 |     9  (23)| 00:00:01 |
|   2 |   NESTED LOOPS OUTER            |         |     1 |    67 |     8  (13)| 00:00:01 |
|*  3 |    HASH JOIN OUTER              |         |     1 |    48 |     7  (15)| 00:00:01 |
|   4 |     NESTED LOOPS                |         |     1 |    44 |     3   (0)| 00:00:01 |
|   5 |      TABLE ACCESS FULL          | TMP_FOO |     1 |    26 |     2   (0)| 00:00:01 |
|   6 |      TABLE ACCESS BY INDEX ROWID| FOO     |     1 |    18 |     1   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | FOO_PK  |     1 |       |     0   (0)| 00:00:01 |
|   8 |     TABLE ACCESS FULL           | BAR     |     1 |     4 |     3   (0)| 00:00:01 |
|   9 |    TABLE ACCESS BY INDEX ROWID  | GEE     |     1 |    19 |     1   (0)| 00:00:01 |
|* 10 |     INDEX UNIQUE SCAN           | GEE_PK  |     1 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Oracle9i Release 9.2.0.1.0 - 64-разрядная версия:

----------------------------------------------------------------------------
| Id  | Operation               |  Name    | Rows  | Bytes |TempSpc| Cost  |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |    98M|  6546M|       |  3382K|
|   1 |  SORT UNIQUE            |          |    98M|  6546M|    14G|  1692K|
|*  2 |   HASH JOIN OUTER       |          |    98M|  6546M|   137M|  2874 |
|   3 |    VIEW                 |          |  2401K|   109M|       |   677 |
|*  4 |     HASH JOIN OUTER     |          |  2401K|   169M|    40M|   677 |
|   5 |      VIEW               |          |   587K|    34M|       |    24 |
|*  6 |       HASH JOIN         |          |   587K|    34M|       |    24 |
|   7 |        TABLE ACCESS FULL| TMP_FOO  |  8168 |   207K|       |    10 |
|   8 |        TABLE ACCESS FULL| FOO      |  7188 |   245K|       |     9 |
|   9 |      TABLE ACCESS FULL  | BAR      |   409 |  5317 |       |     1 |
|  10 |    TABLE ACCESS FULL    | GEE      |  4084 | 89848 |       |     5 |
----------------------------------------------------------------------------

Насколько я могу судить, индексы существуют и верны. Какие у меня есть варианты, чтобы Oracle 9i использовал их?

Обновление № 1: TMP_FOO - временная таблица, в которой нет строк в этом тесте. FOO - обычная таблица с 13 035 строками в моем локальном XE; не уверен, почему план запроса показывает 1 , возможно, он понимает, что ВНУТРЕННЕЕ СОЕДИНЕНИЕ с пустой таблицей не потребует полного сканирования таблицы: -?

Обновление № 2: Я потратил пару недель, пытаясь все и ничего обеспечил реальное улучшение: переписывание запросов, подсказки оптимизатора, изменения в Проектирование БД, избавление от временных таблиц ... Наконец, я получил копию той же неотправленной версии Oracle 9.2.0.1.0, которую заказчик имеет (с очевидным отличием архитектуры), установил ее на своем сайте и ... удивил! В моем 9i все планы выполнения приходят мгновенно, а выполнение запросов занимает от 1 до 10 секунд.

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

Ответы [ 5 ]

6 голосов
/ 19 мая 2011

похоже, что у вас нет данных в вашей 10g экспресс-базе данных, или ваша статистика собрана неправильно.В любом случае Oracle выглядит так, как будто строк не так много, и, следовательно, сканирование диапазона индекса подходит.

В вашей базе данных 9i статистика выглядит так, как будто она собрана правильно, и Oracle видит 4-объединение таблиц с большим количеством строк и без предложения where .В этом случае, так как вы не предоставили подсказку, Oracle создает план объяснения с поведением оптимизатора ALL_ROWS по умолчанию: Oracle найдет план, который является наиболее производительным, чтобы возвращать всех строк в последнюю.В этом случае HASH JOIN с полным сканированием таблиц является чрезвычайно эффективным, он будет возвращать большие наборы строк быстрее, чем с индексным соединением NESTED LOOP.

Возможно, вы захотите использовать индекс, потому что вас интересует толькопервые несколько строк запроса.В этом случае используйте подсказку /*+ FIRST_ROWS*/, которая поможет Oracle понять, что вас больше интересует время ответа первой строки, чем общее общее время запроса.

Возможно, вы захотите использовать индекс, потому что думаете, что это приведет кв более быстрое общее время запроса.Вы можете форсировать план объяснения с помощью подсказок, таких как USE_NL и USE_HASH, но в большинстве случаев вы увидите, что если статистика актуальна,Оптимизатор выберет наиболее эффективный план.


Обновление : я видел ваше обновление о TMP_FOO как о временной таблице без строки.Проблема с временной таблицей состоит в том, что у них нет статистики, поэтому мой ответ выше не подходит для временных таблиц.Поскольку временная таблица не имеет статистики, Oracle должен сделать предположение (здесь он выбирает совершенно произвольно 8168 строк), что приводит к неэффективному плану.

В этом случае целесообразно использовать подсказки.У вас есть несколько вариантов:

  • Сочетание подсказок LEADING , USE_NL и USE_HASH может заставить конкретный план (LEADING, чтобы установить порядок объединений, и USE *, чтобы установить объединение.method).
  • Вы можете использовать недокументированную подсказку CARDINALITY, чтобы предоставить оптимизатору дополнительную информацию, как описано в статье AskTom .Хотя подсказка недокументирована, возможно, безопасно использовать .Примечание: на 10g + DYNAMIC_SAMPLING может быть документированной альтернативой.
  • Вы также можете заранее установить статистику для временной таблицы с помощью процедуры DBMS_STATS.set_table_stats .Этот последний вариант был бы весьма радикальным, поскольку он потенциально мог бы изменить план всех запросов к этой временной таблице.
4 голосов
/ 19 мая 2011

Может быть, 9i делает это совершенно правильно.Согласно опубликованной статистике, база данных Oracle 9i считает, что имеет дело с оператором, возвращающим 98 миллионов строк, тогда как база данных 10G считает, что она вернет 1 строку.Может быть так, что оба они верны, то есть объем данных в двух базах данных очень сильно отличается.Или же вам может потребоваться собрать статистику в одной или обеих базах данных, чтобы получить более точный план запроса.

3 голосов
/ 19 мая 2011

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

Если у вас хорошие отношения с вашим клиентом, вы можете попросить его экспортировать свою статистику, используя DBMS_STATS.EXPORT_SCHEMA_STATS () .Затем вы можете импортировать статистику, используя соответствующую процедуру IMPORT_SCHEMA_STATS.

В противном случае вам придется самостоятельно подделывать числа, используя процедуру DBMS_STATS.SET_TABLE_STATISTICS (). Узнайте больше .

1 голос
/ 19 мая 2011

Вы можете добавить следующие подсказки, которые «заставят» Oracle использовать ваши индексы (если это возможно):

Select /*+ index (FOO FOO_PK) */
       /*+ index (GEE GEE_PK) */
From ...

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

0 голосов
/ 14 июля 2011

Клиент изменил настройку по умолчанию для поддержки очень старого стороннего устаревшего приложения: статический параметр OPTIMIZER_FEATURES_ENABLE был изменен со значения по умолчанию в 9i (9.2.0) на 8.1.7.

Я сделал то же самое изменение в локальной копии 9i, и у меня возникли те же проблемы: объясните планы, для которых требуются часы, и т. Д.

(Зная это, язадал связанный вопрос на ServerFault , но я считаю, что это решает первоначальный вопрос.)

...