Индексы, EXPLAIN PLAN и доступ к записям в Oracle SQL - PullRequest
5 голосов
/ 17 ноября 2011

Я изучал индексы в Oracle SQL, и я хотел провести небольшой эксперимент с тестовой таблицей, чтобы увидеть, как на самом деле работают индексы. Как я обнаружил в предыдущем посте, сделанном здесь, лучший способ сделать это с помощью EXPLAIN PLAN. Однако я сталкиваюсь с чем-то, что меня смущает.

Моя таблица примеров содержит атрибуты (EmpID, Fname, Lname, Occupation, .... и т. Д.). Я заполнил его 500 000 записей, используя Java-программу, которую я написал (случайные имена, профессии и т. Д.). Теперь вот несколько примеров запросов с индексами и без:

НЕТ ИНДЕКСА:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR';

ОБЪЯСНЯЕТ ПЛАН говорит:

OPERATION                         OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE  ANALYZED  1169

Теперь я создаю индекс:

CREATE INDEX occupation_idx
    ON EMPLOYEE (Occupation);

С ИНДЕКСОМ "creation_idx":

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR';

ОБЪЯСНЯЕТ ПЛАН говорит:

OPERATION                         OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE  ANALYZED  1169

Итак ... стоимость ЕЩЕ одинакова, 1169? Сейчас я пытаюсь это:

С ИНДЕКСОМ "creation_idx":

SELECT Occupation FROM EMPLOYEE WHERE Occupation = 'DOCTOR';

ОБЪЯСНИТЕ ПЛАН говорит:

OPERATION                              OPTIMIZER COST
INDEX(RANGE SCAN) TEST.OCCUPATION_IDX  ANALYZED  67

Итак, похоже, что индекс используется только тогда, когда этот столбец является единственным, из которого я извлекаю значения. Но я думал, что целью индекса было разблокировать всю запись, используя индексированный столбец в качестве ключа? Поиск выше довольно бессмысленный ... он ищет значения, которые вы уже знаете. Единственный полезный запрос, о котором я могу думать, который включает в себя ТОЛЬКО значение индексированного столбца (а не остальную часть записи), - это агрегат, такой как COUNT или что-то в этом роде.

Чего мне не хватает?

Ответы [ 4 ]

5 голосов
/ 17 ноября 2011

Даже с вашим индексом Oracle решила выполнить полное сканирование второго запроса.

Почему это произошло?Oracle создаст два плана и определит стоимость каждого: -

1) Полное сканирование

2) Доступ по индексу

Oracle выбрала план с более низкой стоимостью,Очевидно, что полное сканирование было предложено как более низкая стоимость.

Если вы хотите увидеть стоимость плана индекса, вы можете сделать план объяснения с такой подсказкой, чтобы заставить использование индекса:

SELECT /*+ INDEX(EMPLOYEE occupation_idx) */ Fname
FROM EMPLOYEE WHERE Occupation = 'DOCTOR';

Если вы выполните план объяснения по вышеуказанному, вы увидите, что стоимость превышает стоимость полного сканирования.Вот почему Oracle не решила использовать индекс.

Простой способ оценить стоимость плана индекса: -

  • уровень индекса (сколько блоковдолжен быть прочитан сверху вниз)
  • Количество блоков таблицы, которые должны быть впоследствии прочитаны для записей, совпадающих в индексе.Это зависит от оценки Oracle числа сотрудников, имеющих профессию «ДОКТОР».В вашем простом примере это будет:

    количество строк / количество отдельных значений

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

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

SELECT COUNT(*), COUNT(DISTINCT( Occupation ))
FROM EMPLOYEE;

Это позволит людям комментировать стоимость плана индекса.

3 голосов
/ 17 ноября 2011

Я думаю, что вижу, что здесь происходит.

Когда у вас есть индекс, и вы делаете:

SELECT Occupation FROM EMPLOYEE WHERE Occupation = 'DOCTOR';

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

Однако, когда вы делаете:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR';

затем, если Oracle использует индекс, он выполнит сканирование индекса INDEX RANGE, а затем TABLE ACCESS BY ROWID, чтобы найти имя F, соответствующее этому занятию.Теперь, в зависимости от того, сколько строк имеет DOCTOR для Occupation, Oracle придется совершить одну или несколько поездок в таблицу, чтобы найти Fname.Если, например, у вас есть таблица, и для всех сотрудников установлено значение «Занятие» - «DOCTOR», индекс будет бесполезен, и Oracle просто выполнит полный просмотр таблицы.Если в компании 10 000 сотрудников, и только один из них является ДОКТОРОМ, то, опять же, это не сложно, и Oracle будет использовать этот индекс.

Но есть некоторые тонкости, когда вы находитесь где-то между этими двумя крайностями,Людям нравится говорить о «избирательности», т. Е. О том, сколько строк идентифицируется индексом, в зависимости от размера таблицы, когда обсуждают, будет ли индекс использоваться.Но это не действительно правда.Oracle действительно заботится о селективности блоков .То есть, сколько блоков нужно посетить, чтобы удовлетворить запрос?Итак, во-первых, насколько «широкий» диапазон сканирования?Чем более ограничен диапазон значений, указанных значениями предиката, тем лучше.Во-вторых, когда ваш запрос должен выполнить поиск в таблице, сколько разных блоков ему нужно будет посетить, чтобы найти все необходимые ему данные.То есть насколько «случайными» являются данные в таблице относительно порядка индекса?Это называется CLUSTERING_FACTOR.Если вы проанализируете индекс для сбора статистики, а затем посмотрите на USER_INDEXES, вы увидите, что CLUSTERING_FACTOR теперь заполнен.

Итак, что такое CLUSTERING_FACTOR?CLUSTERING_FACTOR - это упорядоченность таблицы по отношению к ключевым столбцам индекса.Значение CLUSTERING_FACTOR всегда будет между числом блоков в таблице и количеством строк в таблице.A low CLUSTERING_FACTOR, то есть тот, который очень близок к числу блоков в таблице, указывает на очень упорядоченную таблицу относительно индекса. high CLUSTERING_FACTOR, то есть тот, который очень близок к числу строк в таблице, очень неупорядочен по отношению к индексу.

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

Итак, почему я потратил так много времени на описание CLUSTERING_FACTOR? Потому что, если у вас есть план выполнения, выполняющий сканирование индекса INDEX RANGE, за которым следует TABLE ACCESS BY ROWID, вы можете быть уверены, что CLUSTERING_FACTOR был рассмотрен оптимизатором Oracle для составления плана выполнения. Например, предположим, что у вас есть таблица из 10000 строк, и предположим, что 100 строк имеют Occupation = 'DOCTOR' Вы пишете запрос выше, спрашивая имя и фамилию сотрудников, чья профессия - ДОКТОР. Что ж, Oracle может очень легко и эффективно определить значения строк строк, в которых занятие - DOCTOR. Но сколько блоков table нужно посетить Oracle, чтобы выполнить поиск по Fname? Это может быть только 1 или 2 табличных блока, если данные кластеризованы (упорядочены) по профессии в таблице. Но это может быть целых 100, если данные очень неупорядочены в таблице! Итак, снова 10000 строк таблицы и, допустим, (в целях иллюстрации и простой математики), что таблица имеет 100 строк / блок и, таким образом, 100 блоков. В зависимости от порядка таблиц (например, CLUSTERING_FACTOR) число посещений блоков таблицы может составлять всего 1 или 100.

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

2 голосов
/ 17 ноября 2011

Индекс - это копия таблицы, в которой хранятся только следующие данные:

  • Индексированные поля (поля)
  • Указатель на исходную строку (rowid).

Скажем, у вас есть такая таблица:

rowid    id  name  occupation
[1]      1   John  clerk
[2]      2   Jim   manager
[3]      3   Jane  boss

Тогда индекс на occupation будет выглядеть так:

occupation  rowid
boss        [3]
manager     [2]
clerk       [1]

, сзаписи отсортированы по occupation в B-Tree.

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

Если вывыберите что-нибудь, кроме occupation:

SELECT  *
FROM    mytable
WHERE   occupation = 'clerk'

, тогда движок должен сделать две вещи: сначала найти соответствующие записи в индексе, во-вторых, найти записи в исходной таблице с помощью rowid.Это как если бы вы объединили две таблицы в rowid.

Так как в индексе строки не в порядке, чтение исходной таблицы не является последовательным и может быть медленным.Может быть быстрее прочитать исходную таблицу в последовательном порядке и просто отфильтровать записи с помощью occupation = 'clerk'.

. Механизм не «разблокирует» записи: он просто находит идентификатор строки в индексе, и еслине хватает данных в самом индексе, он ищет данные в исходной таблице по найденному rowid.

0 голосов
/ 17 ноября 2011

В качестве WAG.Проанализируйте таблицу и индекс, а затем посмотрите, изменится ли план.

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

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