Oracle - понимание подсказки no_index - PullRequest
4 голосов
/ 20 января 2011

Я пытаюсь понять, как no_index на самом деле ускоряет запрос, и я не смог найти документацию в Интернете, чтобы объяснить это.

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

select  * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2)

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

select  /*+ NO_INDEX(TAB_000000000019) */ * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2) 

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

Спасибо!


** Обновление ** Вот что я знаю о таблице в примере.

  • Это «секционированная таблица»
  • TAB_000000000019 - это таблица, а не столбецв нем
  • поле1 индексируется

Ответы [ 6 ]

9 голосов
/ 20 января 2011

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

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

Если администратор базы данных / разработчик знает об этом, он может дать оптимизатору подсказки (что и есть NO_INDEX), сказав ему не использовать данный индекс, поскольку известно, что он замедляет работу, часто из-заdate stats.

В вашем примере TAB_000000000019 будет ссылаться на индекс или таблицу (я предполагаю индекс, так как он выглядит как автоматически сгенерированное имя).

Этонемного чёрного искусства, если честно, но в этом суть, насколько я понимаю.

Отказ от ответственности: я не администратор баз данных, но я баловался в этой области.

3 голосов
/ 21 января 2011

За ваше обновление: если field1 является единственным индексированным полем, то исходный запрос, вероятно, выполнял быструю полную проверку этого индекса (т.е. просматривал каждую запись в индексе и проверял условия фильтра в field1), затем используя эти результаты, чтобы найти строки в таблице и отфильтровать другие условия. Условия для поля field1 таковы, что сканирование уникального индекса или сканирование диапазона (т. Е. Поиск определенных значений или диапазонов значений в индексе) будет невозможно.

Вероятно, оптимизатор выбрал этот путь, потому что в field1 есть два предиката фильтра. Оптимизатор вычислит предполагаемую селективность для каждого из них, а затем умножит их, чтобы определить их совокупную селективность. Но во многих случаях это значительно недооценивает количество строк, соответствующих условию.

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

2 голосов
/ 29 июня 2012

Что касается индексов, следует отметить, что они являются предварительно вычисленными значениями на основе порядка строк и данных в поле. В этом конкретном случае вы говорите, что field1 проиндексирован, и вы используете его в запросе следующим образом:

    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString'

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

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

Это медленно, потому что сравнивает строку и ее подстроки:

            field1_ like '%someGenericString%'

Хотя следующее быстрее, потому что оно специфично:

            field1_ like 'someSpecificString'

Так что причина использовать подсказку NO_INDEX в том, что если у вас есть сравнения по индексу, которые замедляют процесс. Если поле индекса сравнивать с более конкретными данными, тогда сравнение индекса будет обычно быстрее.

Я говорю обычно , потому что, когда индексированное поле содержит больше избыточных данных, как в приведенном выше примере @Atish, ему придется пройти длинный список негативов сравнения, прежде чем будет возвращено положительное сравнение. Подсказки дают разные результаты, поскольку как дизайн базы данных, так и данные в таблицах влияют на скорость выполнения запроса. Таким образом, чтобы применить подсказки, вам нужно знать, будут ли отдельные сравнения, которые вы подсказываете оптимизатору, быстрее в вашем наборе данных. В этом процессе нет ярлыков. Применение подсказок должно происходить после написания правильных запросов SQL, поскольку подсказки должны основываться на реальных данных.

Проверьте эту ссылку подсказки: http://docs.oracle.com/cd/B19306_01/server.102/b14211/hintsref.htm

2 голосов
/ 20 января 2011

Использование индекса снижает производительность запроса, если это приводит к больше дискового ввода-вывода по сравнению с запросом к таблице с индексом.

Это можно продемонстрировать с помощью простой таблицы:

create table tq84_ix_test (
  a number(15) primary key,
  b varchar2(20),
  c number(1)
);

Следующий блок заполняет 1 миллион записей в этой таблице.Каждая 250-я запись заполняется rare value в столбце b, а все остальные заполняются frequent value:

declare
  rows_inserted number := 0;
begin

  while rows_inserted < 1000000  loop

        if mod(rows_inserted, 250) = 0 then

           insert into tq84_ix_test values (
               -1 * rows_inserted, 
               'rare value',
                1);

            rows_inserted := rows_inserted + 1;

        else

           begin
              insert into tq84_ix_test values (
                 trunc(dbms_random.value(1, 1e15)),
                'frequent value',
                 trunc(dbms_random.value(0,2))
               );
               rows_inserted := rows_inserted + 1;

           exception when dup_val_on_index then 
               null;
           end;

        end if;

  end   loop;

end;
/

Индекс ставится в столбец

create index tq84_index on tq84_ix_test (b);

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

set timing on


select /*+ no_index(tq84_ix_test) */
    sum(c)
  from 
    tq84_ix_test
  where
    b = 'frequent value';


select /*+ index(tq84_ix_test tq84_index) */
    sum(c)    
  from 
    tq84_ix_test
  where
    b = 'frequent value';

Почему это?В случае без индекса все блоки базы данных читаются в последовательном порядке.Обычно это дорого и поэтому считается плохим.В нормальной ситуации с индексом такое «полное сканирование таблицы» может быть сведено к чтению, скажем, от 2 до 5 блоков базы данных индекса плюс чтение одного блока базы данных, который содержит запись, на которую указывает индекс.В приведенном здесь примере все совершенно иначе: весь индекс читается, и для (почти) каждой записи в индексе также читается блок базы данных.Таким образом, читается не только вся таблица, но и индекс.Обратите внимание, что это поведение было бы другим, если бы c было также в индексе, потому что в этом случае Oracle мог бы выбрать получение значения c из индекса вместо того, чтобы идти в обход таблицы.

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

0 голосов
/ 06 февраля 2013

Я где-то читал, что использование% перед запросом типа "% someGenericString%" приведет к тому, что Oracle игнорирует INDEX для этого поля.Возможно, это объясняет, почему запрос выполняется медленно.

0 голосов
/ 22 января 2011

В дополнение к тому, что сказали Рене и Дейв, я действительно наблюдал в производственной ситуации:

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

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

К сожалению, один конкретный код региона составлял 90% записей в таблицах.

Пока отчет выполнялся для одного из других (второстепенных) кодов регионов, он был выполнен менее чем за 30 минут., но для основного кода региона это заняло много часов.

Добавление подсказки к SQL для принудительного полного сканирования таблицы решило проблему.

Надеюсь, это поможет.

...