Вопрос плана Oracle Anti-Join Execution - PullRequest
1 голос
/ 13 января 2010

У нас есть две таблицы, такие как:

Event
    id
    type
    ... a bunch of other columns

ProcessedEvent
    event_id
    process

Есть индексы, определенные для

  • Событие (id) (PK)
  • ProcessedEvent (event_id, process)

Первый представляет события в приложении.

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

Чтобы найти все события, требующие обработки, мы выполняем следующий запрос:

select * // of course we do name the columns in the production code
from Event
where type in ( 'typeA', 'typeB', 'typeC')
and id not in (
    select event_id
    from ProcessedEvent
    where process = :1  
)

Статистика актуальна

Поскольку большинство событий обрабатываются, я думаю, что лучший план выполнения должен выглядеть примерно так

  • полное сканирование индекса по индексу ProcessedEvent
  • полное сканирование индекса по индексу событий
  • анти-соединение между двумя
  • доступ к столу с остальными
  • фильтр

Вместо этого Oracle делает следующее

  • полное сканирование индекса по индексу ProcessedEvent
  • полное сканирование таблицы на таблице событий
  • фильтр таблицы событий
  • анти-соединение между двумя наборами

С указанием индекса я заставляю Oracle делать следующее:

  • полное сканирование индекса по индексу ProcessedEvent
  • полное сканирование индекса по индексу событий
  • таблица доступа к таблице событий
  • фильтр таблицы событий
  • анти-соединение между двумя наборами

что действительно глупо ИМХО.

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


Дополнение: Производительность плохая. Мы решаем проблему с производительностью, выбирая только Event.ID и затем выбирая нужные строки «вручную». Но, конечно, это всего лишь обходной путь.

Ответы [ 4 ]

2 голосов
/ 13 января 2010

ваш FULL INDEX SCAN, вероятно, будет быстрее, чем FULL TABLE SCAN, так как индекс, вероятно, «тоньше» таблицы. Тем не менее, FULL INDEX SCAN представляет собой полное чтение сегмента и будет стоить примерно столько же, сколько FULL TABLE SCAN.

Однако вы также добавляете шаг TABLE ACCESS BY ROWID. Это дорогостоящий шаг: один логический IO на строку для доступа к ROWID, тогда как вы получаете один логический IO на мультиблоки (в зависимости от db_file_multiblock_read_count parameter) для FULL TABLE SCAN.

В заключение оптимизатор вычисляет, что:

cost(FULL TABLE SCAN) < cost(FULL INDEX SCAN) + cost(TABLE ACCESS BY ROWID)

Обновление : FULL TABLE SCAN также включает фильтр по типу раньше, чем в пути FULL INDEX SCAN (поскольку INDEX не знает, какой тип является событием), уменьшая тем самым размер набор, который будет против соединения (еще одно преимущество полного сканирования таблицы).

0 голосов
/ 10 октября 2013

Что-то вроде:

WITH
  PROCEEDED AS
  (
    SELECT
      event_id
    FROM
      ProcessedEvent
    WHERE
      PROCESS = :1
  )
SELECT
  * // of course we do name the columns in the production code
FROM
  EVENT
LEFT JOIN PROCEEDED P
ON
  p.event_id = EVENT.event_id
WHERE
  type           IN ( 'typeA', 'typeB', 'typeC')
  AND p.event_id IS NULL; -- exclude already proceeded

может работать достаточно быстро (по крайней мере, намного быстрее, чем NOT IN).

0 голосов
/ 13 января 2010

Я не могу объяснить поведение оптимизатора, но мой опыт заключался в том, чтобы любой ценой избегать «НЕ В», заменив его вместо МИНУС, например, так:

select * from Event
where id in (
  select id from Event where type in ( 'typeA', 'typeB', 'typeC')
 minus
  select id from ProcessedEvent
)

Я видел порядки величины в производительности запросов с похожими преобразованиями.

0 голосов
/ 13 января 2010

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

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

Производительность плохая, или вы просто спрашиваете, почему оптимизатор это сделал?

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