Почему Oracle игнорирует «идеальный» индекс? - PullRequest
4 голосов
/ 14 ноября 2009

У меня есть эта таблица:

create table demo (
    key number(10) not null,
    type varchar2(3) not null,
    state varchar2(16) not null,
    ... lots more columns ...
)

и этот индекс:

create index demo_x04 on demo(key, type, state);

Когда я запускаю этот запрос

select * from demo where key = 1 and type = '003' and state = 'NEW'

EXPLAIN PLAN показывает, что выполняется полное сканирование таблицы. Поэтому я опустил индекс и создал его снова. EXPLAIN PLAN по-прежнему говорит полное сканирование таблицы. Как это может быть?

Немного предыстории: это исторические данные, поэтому происходит поиск строки с состоянием CLEARED и вставка новой строки с состоянием NEW (плюс я копирую несколько значений из старой строки). Старая строка затем обновляется до USED. Так что стол всегда растет. Что я заметил, так это то, что индекс мощности равен 0 (несмотря на то, что у меня есть тысячи различных значений). После воссоздания количество элементов возросло, но CBO индекс не понравился больше.

На следующее утро Oracle внезапно полюбил индекс (вероятно, спал над ним) и начал использовать его, но ненадолго. Через некоторое время обработка упала с 50 строк / с до 3 строк / с, и я снова увидел «FULL TABLE SCAN». Что происходит?

В моем случае мне нужно обработать около миллиона строк. Я совершаю изменения в партиях ок. 50. Есть ли какая-нибудь команда, которую я должен выполнить после коммита для обновления / реорганизации индекса или что-то в этом роде?

Я на Oracle 10g.

[РЕДАКТИРОВАТЬ] У меня 969'491 различных ключей в этой таблице, 3 типа и 3 состояния.

Ответы [ 5 ]

4 голосов
/ 14 ноября 2009

Что произойдет, если вы укажете указатель индекса? Попробуйте это:

SELECT /*+ INDEX (demo demo_x04) */ * 
  FROM demo 
 WHERE key = 1 
   AND type = '003' 
   AND state = 'NEW';

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

Добавьте подсказку и посмотрите, дает ли EXPLAIN PLAN другой план, а запрос работает лучше.

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

Чтобы обновить статистику для таблицы, используйте пакет dmbs_stats.gather_table_stats .

Например:

exec dbms_stats.gather_table_stats ('владелец', 'DEMO');

3 голосов
/ 15 ноября 2009

"На следующее утро Oracle неожиданно понравился индекс (вероятно, спал над ним)" Возможно, DBMS_STATS работает всю ночь.

Как правило, я вижу одну из трех причин ПОЛНОГО СКАНИРОВАНИЯ над индексом. Во-первых, оптимизатор считает, что таблица пуста или, по крайней мере, очень мала. Я подозреваю, что это была начальная проблема. В этом случае будет быстрее выполнить полное сканирование таблицы, состоящей только из нескольких блоков, а не использовать индекс.

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

"select * from demo where key = 1 and type = '003' and state = 'NEW'"

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

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

3 голосов
/ 14 ноября 2009

Была ли таблица недавно проанализирована? Если Oracle считает, что он очень маленький, он может даже не подумать об использовании индекса.

Попробуйте это:

select last_analyzed, num_rows 
from user_tables
where table_name = 'DEMO';

NUM_ROWS сообщает, сколько строк Oracle считает в таблице.

1 голос
/ 16 ноября 2009

Комментарий к описанной вами обработке: звучит так, будто вы делаете построчную обработку с прерывистыми фиксациями, и я призываю вас переосмыслить это, если можете. Механизм обновления / вставки вполне может быть преобразован в оператор MERGE, и весь набор данных затем может быть обработан в одном операторе с одним коммитом в конце. Это почти наверняка будет быстрее и потребляет меньше ресурсов, чем ваш текущий метод.

0 голосов
/ 14 ноября 2009

Значение ключа колонки всегда равно 1? Если это так, я не уверен, что обращение к индексу оптимизировало бы запрос, поскольку в любом случае нужно было бы проверить каждую строку. Если это так, объявите индекс без ключевого столбца. Вы также можете попробовать:

select key, type, state from demo where key = 1 and type = '003' and state = 'NEW'

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

Я только догадываюсь на основании вашего заявления, что индекс показывает количество элементов 0.

...