Pergunta

Temos duas mesas como isso:

Event
    id
    type
    ... a bunch of other columns

ProcessedEvent
    event_id
    process

Existem índices definidos para

  • Evento (ID) (PK)
  • ProcessEdEvent (Event_id, processo)

O primeiro representa eventos em um aplicativo.

O segundo representa o fato de que um determinado evento obteve processos por um determinado processo. Existem muitos processos que precisam processar um determinado evento; portanto, existem várias entradas na segunda tabela para cada entrada no primeiro.

Para encontrar todos os eventos que precisam de processamento, executamos a seguinte consulta:

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  
)

As estatísticas estão atualizadas

Como a maioria dos eventos é processada, acho que o melhor plano de execução deve parecer algo assim

  • Índice Completo de Índice no Índice ProcessEvent
  • Índice Completo de Índice no Índice de Eventos
  • anti -junção entre os dois
  • Acesso à tabela com o resto
  • filtro

Em vez disso, o Oracle faz o seguinte

  • Índice Completo de Índice no Índice ProcessEvent
  • Tabela completa digitalização na tabela de eventos
  • filtre a tabela de eventos
  • anti -junção entre os dois conjuntos

Com uma dica de índice, recebo o Oracle para fazer o seguinte:

  • Índice Completo de Índice no Índice ProcessEvent
  • Índice Completo de Índice no Índice de Eventos
  • Tabela Acce na tabela de eventos
  • filtre a tabela de eventos
  • anti -junção entre os dois conjuntos

O que é realmente estúpido IMHO.

Portanto, minha pergunta é: qual pode ser o motivo do Oracle insistir no acesso à tabela inicial?


Adição: o desempenho é ruim. Estamos corrigindo o problema de desempenho selecionando apenas o evento. Mas é claro que isso é apenas um trabalho por aí.

Foi útil?

Solução

Sua varredura completa do índice provavelmente será mais rápida que uma varredura completa, já que o índice provavelmente é "mais fino" que a tabela. Ainda assim, a verificação completa do índice é uma leitura completa do segmento e será o mesmo custo da varredura completa da tabela.

No entanto, você também está adicionando um acesso à tabela por etapa rowid. É um passo caro: um io lógico por linha Para o acesso a Rowid, enquanto você obtém um io lógico por multi blocos (dependendo do seu db_file_multiblock_read_count parameter) para a varredura completa da tabela.

Em conclusão, o otimizador calcula que:

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

Atualizar: A varredura completa da tabela também permite o filtro no tipo mais cedo do que no caminho de varredura de índice completo (como o índice não sabe que tipo é um evento), reduzindo o tamanho do conjunto que será anti-jacuncional (ainda mais vantagem da varredura completa da tabela).

Outras dicas

O otimizador faz muitas coisas que não fazem sentido a princípio, mas têm suas razões. Eles nem sempre são certo, mas eles são compreensíveis.

A tabela de eventos pode ser mais fácil para variar em pleno e não pelo RowID Access devido ao seu tamanho. Pode ser que haja significativamente menos operações de IO envolvidas para ler toda a tabela sequencialmente do que ler bits e peças.

O desempenho é ruim, ou você está apenas perguntando por que o otimizador fez isso?

Não posso explicar o comportamento do otimizador, mas minha experiência tem sido evitar "não em todo custo", substituindo -o por menos, assim:

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

Eu vi ordens de magnitude no desempenho da consulta com transformações semelhantes.

Algo como:

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

poderia funcionar rápido o suficiente (pelo menos muito mais rápido que NOT IN).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top