Problema de otimização: chaves clusterizadas compostas, condições de sinalização e mesclagem de índice

11

Três mesas:

product: com colunas: ( a, g, ...a_lot_more... )

a: PK, clustered
g: bit-column

main: com colunas: ( c, f, a, b, ...a_lot_more... )

c: PK, clustered
f: bit-column
(a, b): UQ 

lookup com colunas: ( a, b, c, i )

(a, b): PK, clustered
a: FK to product(a)
c: UQ, FK to main(c)
i: bit-column

Não consigo encontrar bons índices para a associação:

FROM  
    product
  JOIN 
    lookup
      ON  lookup.a = product.a  
  JOIN
    main
      ON  main.c = lookup.c 
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

Eu tentei um índice de cobertura product (g, a, ...)e ele é usado, mas não com resultados espetaculares.

Algumas combinações de índices na lookuptabela produzem planos de execução com mesclagem de índices, com leve ganho de eficiência em relação ao plano anterior.

Existe alguma combinação óbvia que estou perdendo?

Um re-design da estrutura poderia ajudar?

O DBMS é o MySQL 5.5 e todas as tabelas estão usando o InnoDB.


Tamanhos da tabela:

product: 67K   ,  g applied:    64K 

main:   420K   ,  f applied:   190K

lookup:  12M   ,  b,i applied:  67K 
ypercubeᵀᴹ
fonte
Tente mover os predicados do filtro para as junções e veja se o otimizador faz algo sensato com isso. Já vi o otimizador do SQL Server falhar nisso antes.
ConcernedOfTunbridgeWells
Parece um produto cartesiano porque não vejo nada JUNTO na tabela de produtos. Ou eu perdi alguma coisa ???
RolandoMySQLDBA
@RolandoMySQLDBA: Você está certo. Eu vou corrigir a consulta.
precisa saber é o seguinte

Respostas:

3

Isso me dói ...

Eu tive que usar tabelas temporárias com o InnoDB antes. Carregue-os com filtros, crie um índice, junte-se a essas tabelas temporárias.

O problema, na minha opinião, é se o InnoDB tiver apenas o algoritmo Nested Join: os otimizadores de consultas RDBMS crescidos têm mais a usar. Isso se baseia na tentativa de executar cargas do tipo Data Warehouse no InnoDB.

As tabelas temporárias arrastam a complexidade geral para baixo do nível do otimizador de consultas MySQL ...

gbn
fonte
Thnx, eu vou tentar isso. O número ou linhas (após a aplicação dos critérios não são tão grandes, 64K, 67K, 190K, respectivamente). Talvez eu devesse tentar me livrar de uma das três tabelas ( main) desnormalizando os dados para lookup?
precisa saber é o seguinte
11
@ypercube: desnormalizar vai fazer linhas mais amplo, menor densidade page = outros problemas
GBN
3

Parece um produto cartesiano. Refazer os critérios JOIN

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON  main.c = lookup.c 
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

SUGESTÃO ALTERNATIVA

Isso pode parecer pouco ortodoxo e provavelmente cheira a SQL Anitpattern, mas aqui vai ...

FROM  
    product
JOIN 
    (
        SELECT * FROM lookup
        WHERE i=1 AND b=17
    ) lookup ON product.a = lookup.a  
JOIN
   main ON main.c = lookup.c 
WHERE 
    product.g = 1 AND main.f = 1

Não movi as subconsultas product.g = 1e main.f = 1para as subconsultas porque são campos de bits e apenas fará uma varredura de tabela no momento. Mesmo se os campos de bits fossem índices, o Query Optimizer simplesmente ignoraria esse índice.

Claro, você pode mudar SELECT * FROM lookuppara SELECT a FROM lookupse o seu SELECT não precisar de nadalookup

Talvez envolva a, b na junção entre pesquisa e main se isso faz sentido

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON  main.a = lookup.a AND main.b = lookup.b
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

ou coloque de volta ce junte-se a três colunas (Índice nas três colunas de maine lookup)

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON main.a = lookup.a
      AND main.b = lookup.b
      AND main.c = lookup.c
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17
RolandoMySQLDBA
fonte
Thnx. Plano EXPLAIN diferente, mas desempenho semelhante.
usar o seguinte código
Qual a cardinalidade do main.fe product.g??? Se a cardinalidade de main.fe product.gpara o valor for 1 for menor que 5% das linhas da tabela, um índice main.fe product.gpoderá ser justificável.
RolandoMySQLDBA
Não importa, eles já estão indexados. Se a cardinalidade de main.fe product.gfor 2, você poderá abandonar esses índices.
RolandoMySQLDBA
Editou a pergunta com tamanhos de tabela e linhas usadas (após as condições serem aplicadas).
precisa saber é o seguinte
Atualizei minha pergunta, sugestão JOINing em a, b em vez de c. Veja se isso faz um plano EXPLAIN diferente
RolandoMySQLDBA