Problème d'optimisation: clés en cluster composées, conditions d'indicateur et fusion d'index

11

Trois tables:

product: avec colonnes: ( a, g, ...a_lot_more... )

a: PK, clustered
g: bit-column

main: avec colonnes: ( c, f, a, b, ...a_lot_more... )

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

lookup avec des colonnes: ( a, b, c, i )

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

Je ne trouve pas de bons index pour la jointure:

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

J'ai essayé un indice de couverture product (g, a, ...)et il est utilisé mais pas avec des résultats spectaculaires.

Certaines combinaisons d'index sur la lookuptable produisent des plans d'exécution avec fusion d'index, avec un léger gain d'efficacité par rapport au plan précédent.

Y a-t-il une combinaison évidente qui me manque?

Une refonte de la structure pourrait-elle aider?

Le SGBD est MySQL 5.5 et toutes les tables utilisent InnoDB.


Tailles de table:

product: 67K   ,  g applied:    64K 

main:   420K   ,  f applied:   190K

lookup:  12M   ,  b,i applied:  67K 
ypercubeᵀᴹ
la source
Essayez de déplacer les prédicats de filtre dans les jointures et voyez si l'optimiseur fait quelque chose de sensé avec cela. J'ai déjà vu l'optimiseur de SQL Server échouer.
ConcernedOfTunbridgeWells
Ressemble à un produit cartésien car je ne vois rien de JOIN dans le tableau des produits. Ou ai-je raté quelque chose ???
RolandoMySQLDBA
@RolandoMySQLDBA: Vous avez raison. Je vais corriger la requête.
ypercubeᵀᴹ

Réponses:

3

Cela me fait mal ...

J'ai déjà dû utiliser des tables temporaires avec InnoDB. Chargez-les avec des filtres, créez un index, rejoignez ces tables temporaires.

Le problème, comme je le pense, est que InnoDB ne dispose que de l'algorithme Nested Join: les optimiseurs de requête RDBMS adultes ont plus à utiliser. Ceci est basé sur la tentative d'exécution de charges de type Data Warehouse sur InnoDB.

Les tables temporaires entraînent la complexité globale au niveau de l'optimiseur de requêtes MySQL ...

gbn
la source
Thnx, je vais essayer ça. Le nombre ou les lignes (une fois les critères appliqués ne sont pas si grands, 64K, 67K, 190K respectivement). Peut-être que je devrais essayer de me débarrasser de l'une des trois tables ( main) en dénormalisant les données dans lookup?
ypercubeᵀᴹ
1
@ypercube: la dénormalisation rendra les lignes plus larges, la densité de page plus faible = autres problèmes
gbn
3

Il ressemble à un produit cartésien. Refaire les critères 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

SUGGESTION ALTERNATIVE

Cela peut sembler peu orthodoxe et sent probablement comme SQL Anitpattern, mais ici ça va ...

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

Je n'ai pas déplacé le product.g = 1et main.f = 1dans les sous-requêtes car ce sont des champs de bits et je vais juste faire un scan de table à ce point. Même si les champs de bits étaient des index, l'Optimiseur de requête ignorerait simplement un tel index.

Bien sûr, vous pouvez changer SELECT * FROM lookupde SELECT a FROM lookupsi votre SELECT ne pas besoin de quoi que ce soitlookup

Peut-être impliquer a, b dans la jointure entre recherche et principal si cela a du sens

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 remettre c et joindre sur trois colonnes (Index sur les trois colonnes dans mainet 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
la source
Thnx. Plan EXPLAIN différent, mais performances similaires.
ypercubeᵀᴹ
Quelle est la cardinalité du main.fet product.g??? Si la cardinalité de main.fet product.gpour la valeur est 1 est inférieure à 5% des lignes du tableau, un index sur main.fet product.gpeut être justifié.
RolandoMySQLDBA
Peu importe, ils sont déjà indexés. Si la cardinalité de main.fet product.gest 2, vous pouvez abandonner ces index.
RolandoMySQLDBA
Modification de la question avec les tailles de tableau et les lignes utilisées (après l'application des conditions).
ypercubeᵀᴹ
J'ai mis à jour ma question, suggestion JOINDRE sur a, b au lieu de c. Voir si cela fait un plan EXPLAIN différent
RolandoMySQLDBA