2016-05-15 6 views
0

Ich testete ein Update zwischen zwei großen (~ 5 mil Datensätze jeweils), die etwa 10 Sekunden pro Update dauerte. So testete tun Erklären für mein erstes Mal der Auswahl:Optimierung Update-Abfrage mit Compound-Index

SELECT 
    T1.Z, T2.Z 
FROM 
    TableB T1 
INNER JOIN TableL T2 
    on T1.Name=T2.Name 
    and T1.C=T2.C 
    and T1.S=T2.S 
    and T1.Number>=T2.MinNumber 
    and T1.Number<=T2.MaxNumber 

zurück Erklären Sie den folgende als mögliche Schlüssel:

  • Namen
  • C
  • S
  • Anzahl

und wählen Sie C als Schlüssel.

Ich wurde gesagt, dass meine beste Wette eine Verbindung Schlüssel und in der Reihenfolge der Auswahl so habe ich

Alter Table TableB Add Index Compound (Name,C,S,Number) 

Und hatte eine Erklärung wieder zu machen, es ist meine Verbindung wählen würde, die Hoffnung, aber jetzt auch obwohl es zeigt die Verbindung Index als möglicher Schlüssel Index C. wählt es noch

ich gelesen, dass ich den Index erzwingen kann ich mit will:

SELECT 
    T1.Z, T2.Z 
FROM TableB T1 Force Index(Compound) 
INNER JOIN TableL T2 
    on T1.Name=T2.Name 
    and T1.C=T2.C 
    and T1.S=T2.S 
    and T1.Number>=T2.MinNumber 
    and T1.Number<=T2.MaxNumber 

aber ich bin nicht sicher, ob es irgendwelche macht Sinn, die Auswahl von MySql zu übertreiben, und wenn es nicht hilft, das Update wird fast zwei Jahre dauern, scheint es nicht eine schlaue Sache zu testen.

Gibt es einen Schritt, den ich vermisse? Muss ich die anderen Schlüssel entfernen, so dass sie meine Verbindung auswählen und wenn ja, wie werde ich wissen, ob es sogar einen Unterschied machen wird (vorausgesetzt, dass Mysql es sah und es zurückwies)?


Ausgang T1 Erklären:(Anmerkung: ich noch nicht die Verbindung Index hinzugefügt haben, wie die Tabelle sehr groß ist und es könnte Zeit verschwendet werden, bis ich das herausfinden.Ich habe es vorher auf einer stark verkürzten Version der Tabelle, aber das wird mit diesem erklärt) Table1

  • SELECT_TYPE nicht helfen: einfach
  • Typ: ref
  • possible_keys:
  • Anzahl, C, S, Namens-
  • Schlüssel: Name
  • key_len: 303
  • ref: func
  • Reihen: 4
  • Extra: SIMPLE
  • Typ: ALLE
  • possible_keys: minNumber, maxNumber
  • Schlüssel wo

für Table2 Erklären

  • SELECT_TYPE mit :
  • key_length:
  • ref:
  • Reihen: 5.447.100
  • Extra:

Cardinality(nur Indizes relevant hier gibt es ein paar andere zeigt):

  • Primär: 5139680

  • Name: 1284920

  • Nummer: 57749

  • C: 7002

  • S: So 21

+0

@Strawberry Bitte beachten Sie mein Update Dies ist keine doppelte Frage noch ist es verdient, zu einem generischen 'Was ist ein Index?' Faden. Vielen Dank. – user3649739

+2

Bitte schreiben Sie die Kardinalität für jeden Index, MySQL wählt einen, der am restriktivsten ist und in den Speicher passt. Bitte posten Sie auch die vollständige erweiterte Ausgabe. – Pentium10

+1

"T1.Name = T2.Name" - Es ist keine Entschuldigung dafür, dass MySQL einen "schlechten" Index wählt - aber es ist auch keine gute Idee, große Tabellen in VARCHAR-Spalten zu verknüpfen. –

Antwort

3

basierend auf einigen großen Kommentare/Eingang I mit einer Lösung kam . Eine Flashbulb-Eingabe von Paul Spiegel war, dass der Versuch, zwei 5 + Mil-Tabellen mit mehreren VarChar-Feldern zu verbinden, nicht empfohlen wurde.

Also was ich getan habe, war ein UniqueTable mit ID und UnqiueRecord Felder zu erstellen.

Ich machte dann den UniqueRecord einen eindeutigen Index.

ich in diese Tabelle eingefügt von beiden TableA und TableB wie:

Insert IGNORE into `Unique` (UniqueRecord) 
Select Concat(Name,C,S) from Table1 Group by Name,C,S; 
Insert IGNORE into `Unique` (UniqueRecord) 
Select Concat(Name,C,S) from Table2 Group by Name,C,S 

Dies gab mir eindeutige Datensätze aus sowohl innerhalb als auch zwischen den beiden Tabellen.

Ich fügte dann ein UniqeRecord_ID-Feld zu Tabelle 1 und Tabelle 2 hinzu.

Ich habe dann ein zwischen jedem Tisch sitzen und die UniqueRecord die UniqueRecord ID für jede Tabelle zu schreiben:

Update Table1 as T1 
Inner Join Unique as T2 
On Concat(T1.Name,T1.S,T1.C) = T2.UniqueRecord 
Set T1.UniqueRecord_ID=T2.ID 

Schließlich habe ich einen Schlüssel zu jeder Tabelle auf UniqueRecord_ID hinzugefügt.

Meine Erklärung zeigte, dass es nur diesen Schlüssel aus T2 aber während es 10 Sekunden pro Datensatz für die Auswahl vor brauchte (ich testete auf 1,10,100 und stoppte dort, da ich nicht die erforderlichen 578 Tage hatte, um die zu testen ganze Tabelle: |) Die gesamte Auswahl, die Rückkehr zu fast 5 Millionen Datensätze dauerte 72 Sekunden.

+0

Das klingt immer noch unglaublich langsam. Welche Hardware verwendest du? – Strawberry

+0

@Strawberry Eine Minute, um ein Update/Join zwischen zwei 5 mil Record-Tabellen einschließlich> = <= Vergleich auf Datensätze durch drei Felder übereinstimmen klingt langsam für Sie? Auf jeden Fall ist es für mich in einer Minute nutzbar gegenüber den 568 Tagen, an denen ich angefangen habe. Wenn es hier hilft, ist die Konfiguration: VM mit 8x Virtual CPU 2.4Ghz 16Gb RAM 300Gb SSD – user3649739

0

Beachten Sie, dass die erste Tabelle (je nachdem, welche sie ist) vollständig gescannt werden muss. Also, das Beste, was wir tun können, ist einen guten Index für die zweite Tabelle zu haben.

Der optimale Index (wie bereits erwähnt) für T1 ist (Name,C,S,Number). Für T2 ist es (Name,C,S,MinNumber,MaxNumber), die sperriger ist.

Der Optimierer scheint mit T1 beginnen zu wollen; vielleicht ist es etwas kleiner. Sagen wir es zwingen, mit T2 zu beginnen, indem INNER JOIN-STRAIGHT_JOIN verändern und tauschen die Reihenfolge:

SELECT 
    T1.Z, T2.Z 
FROM   TableL T2 -- note 
STRAIGHT_JOIN TableB T1 -- note 
    on T1.Name=T2.Name 
    and T1.C=T2.C 
    and T1.S=T2.S 
    and T1.Number>=T2.MinNumber 
    and T1.Number<=T2.MaxNumber 

Dann lassen Sie uns eine weitere Optimierung tun: Wenn Z nicht ‚zu groß‘ ist, wollen wir es am Ende des Index enthalten so dass es ein „Covering Index“ wird:

INDEX(Name,C,S,Number,Z) 

(Name, C, S kann in beliebiger Reihenfolge, aber Number, Z in dieser Reihenfolge, und am Ende werden muss.) Wenn Sie derzeit INDEX(Name) haben, DROP es als überflüssig zu sein.

Dann wird die EXPLAIN sagen, dass Sie eine vollständige Tabelle Scan von T2, plus eine "Using Index" auf T1 tun.

Bitte geben Sie SHOW CREATE TABLE; Möglicherweise gibt es mehr Optimierungen.

+0

Danke werde ich einen Blick auf diese Option werfen, da ich weiterhin mit diesen Tabellen arbeiten. Frage: Warum muss Name, Z in dieser Reihenfolge sein und der Rest ist egal? Bisher habe ich (und ändere den Index, um zu reflektieren) angenommen, dass die Reihenfolge des Indexes unterstützt wird, indem man sie in die Reihenfolge der Kardinalität setzt, also würde ich in Ihrem vorgeschlagenen Fall Name, Z, C, Nummer, S sein. Ist das eine unbegründete Annahme (Ordnung zusammengesetzt durch Kardinalität)? Zum Beispiel hat S eine Kardinalität von 50, während Name eine Kardinalität von 50.000 hat. – user3649739

+0

Kardinalität ignorieren. Ein Index wird zum Schreiben behandelt. Beginnen Sie mit Spalten, die "= konstant" sind (in beliebiger Reihenfolge), und dann optional einen Bereich. Wenn es sich um einen "Deckungsindex" handelt, können die restlichen Spalten in beliebiger Reihenfolge stehen. Vorbehalt: Das ist eine sehr kurze Zusammenfassung der guten Praxis. [_More_] (http://mysql.rjweb.org/doc.php/index_cookbook_mysql) –

+0

Name, C, S sind alle in einem 'WHERE' (' ON' in diesem Fall) ', so dass jede Reihenfolge in Ordnung ist. Dann ist Number in einem "Bereich" (wegen "> =" und "<="). Ich schlug vor, Z anzuheften, um es zu "bedecken". Der Index ist als BTree strukturiert. Es wird basierend auf Name, C, S und dem ersten Wert für Number aufschlüsseln. Dann scannt es vorwärts ("Bereich") bis zur letzten Nummer. Z liefert den gewünschten Wert für die SELECT-Liste ("deckend"). –