Optimierungsproblem: zusammengesetzte Clusterschlüssel, Flag-Bedingungen und Indexzusammenführung

11

Drei Tabellen:

product: mit Spalten: ( a, g, ...a_lot_more... )

a: PK, clustered
g: bit-column

main: mit Spalten: ( c, f, a, b, ...a_lot_more... )

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

lookup mit Spalten: ( a, b, c, i )

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

Ich kann keine guten Indizes für den Join finden:

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

Ich habe einen Covering-Index ausprobiert product (g, a, ...)und er wird verwendet, aber nicht mit spektakulären Ergebnissen.

Einige Kombinationen von Indizes in der lookupTabelle erzeugen Ausführungspläne mit Indexzusammenführung mit einem leichten Effizienzgewinn gegenüber dem vorherigen Plan.

Gibt es eine offensichtliche Kombination, die mir fehlt?

Könnte eine Neugestaltung der Struktur helfen?

Das DBMS ist MySQL 5.5 und alle Tabellen verwenden InnoDB.


Tischgrößen:

product: 67K   ,  g applied:    64K 

main:   420K   ,  f applied:   190K

lookup:  12M   ,  b,i applied:  67K 
ypercubeᵀᴹ
quelle
Versuchen Sie, die Filterprädikate in die Verknüpfungen zu verschieben, und prüfen Sie, ob der Optimierer damit etwas Sinnvolles tut. Ich habe gesehen, dass der Optimierer von SQL Server diesbezüglich zuvor fehlgeschlagen ist.
ConcernedOfTunbridgeWells
Sieht aus wie ein kartesisches Produkt, da in der Produkttabelle nichts angezeigt wird. Oder habe ich etwas verpasst ???
RolandoMySQLDBA
@ RolandoMySQLDBA: Sie haben Recht. Ich werde die Abfrage korrigieren.
Ypercubeᵀᴹ

Antworten:

3

Das schmerzt mich ...

Ich musste vorher temporäre Tabellen mit InnoDB verwenden. Laden Sie sie mit Filtern, erstellen Sie einen Index und verbinden Sie diese temporäre Tabelle.

Ich denke, das Problem ist, wenn diese InnoDB nur über einen Nested Join-Algorithmus verfügt: Die erwachsenen RDBMS-Abfrageoptimierer haben mehr zu verwenden. Dies basiert auf dem Versuch, Ladevorgänge vom Typ Data Warehouse in InnoDB auszuführen.

Temp-Tabellen ziehen die Gesamtkomplexität auf die Ebene des MySQL-Abfrageoptimierers ...

gbn
quelle
Danke, das werde ich versuchen. Die Anzahl oder Zeilen (nachdem die Kriterien angewendet wurden, sind sie nicht so groß, 64 KB, 67 KB bzw. 190 KB). Vielleicht sollte ich versuchen, eine der drei Tabellen ( main) durch Denormalisierung von Daten zu entfernen lookup?
Ypercubeᵀᴹ
1
@ypercube: Denormalisierung macht Zeilen breiter, geringere
Seitendichte
3

Es sieht aus wie ein kartesisches Produkt. Wiederholen Sie die JOIN-Kriterien

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

ALTERNATIVER VORSCHLAG

Dies mag unorthodox erscheinen und riecht wahrscheinlich nach SQL Anitpattern, aber hier geht es ...

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

Ich habe das product.g = 1und nicht main.f = 1in Unterabfragen verschoben, da es sich um Bitfelder handelt und nur an dieser Stelle ein Tabellenscan durchgeführt wird. Selbst wenn die Bitfelder Indizes wären, würde das Abfrageoptimierungsprogramm einen solchen Index einfach ignorieren.

Natürlich können Sie zu wechseln SELECT * FROM lookup, SELECT a FROM lookupwenn Ihr SELECT nichts von benötigtlookup

Nehmen Sie möglicherweise a, b in die Verbindung zwischen Lookup und Main auf, wenn dies sinnvoll ist

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

oder setzen Sie c zurück und verbinden Sie drei Spalten (Index der drei Spalten in mainund 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
quelle
Danke. Anderer EXPLAIN-Plan, aber ähnliche Leistung.
Ypercubeᵀᴹ
Was ist die Kardinalität der main.fund product.g??? Wenn die Kardinalität von main.fund product.gfür den Wert 1 weniger als 5% der Tabellenzeilen beträgt, ist ein Index für main.fund product.gmöglicherweise gerechtfertigt.
RolandoMySQLDBA
Egal, sie sind bereits indiziert. Wenn die Kardinalität von main.fund product.g2 ist, können Sie diese Indizes fallen lassen.
RolandoMySQLDBA
Die Frage wurde mit den verwendeten Tabellengrößen und Zeilen bearbeitet (nachdem die Bedingungen angewendet wurden).
Ypercubeᵀᴹ
Ich habe meine Frage aktualisiert und vorgeschlagen, mich a, b anstelle von c anzuschließen. Sehen Sie, ob dies einen anderen EXPLAIN-Plan
ergibt