2010-08-16 12 views
5

Ich habe folgendes Szenario: In einer MySQL-Datenbank habe ich 2 MyISAM-Tabellen, eine mit 4,2 Millionen Zeilen und eine weitere mit 320 Millionen Zeilen. Im Folgenden ist das Schema für die Tabellen:MySQL langsame Abfrage mit Join obwohl EXPLAIN zeigt guten Plan

Tabelle 1 (4.2M Reihen)

F1 INTEGER UNSIGNED NOT NULL PRIMARY KEY 
f2 varchar(40) 
f3 varchar(40) 
f4 varchar(40) 
f5 varchar(40) 
f6 smallint(6) 
f7 smallint(6) 
f8 varchar(40) 
f9 varchar(40) 
f10 smallint(6) 
f11 varchar(10) 
f12 tinyint(4) 
f13 smallint(6) 
f14 text 

Table2 (320M Reihen)

F1 INTEGER UNSIGNED NOT NULL PRIMARY KEY 
f2 INTEGER UNSIGNED NOT NULL 

Table2 in einer anderen Datenbank ist, aber ich bin mit einer gespeicherten Prozedur, die die zwei Tabellen abfragt. Die Beziehung zwischen den beiden Tabellen ist, dass für Table1.F1 es bis zu ungefähr sein kann. 100 Zeilen in Tabelle2.F1 (Fremdschlüssel), die übereinstimmen, und der Wert für Table2.f2 wird für diese übereinstimmenden Schlüssel zurückgegeben. Ich habe einen Index IX1 (f2 (15), f3 (10)) auf Tabelle 1 und ein Index IX2 (F1, F2) und IX3 (f2) in Tabelle 2

Die Abfragen mir folgendes leite sind:

SELECT g.F1 
FROM DB1.Table1 g 
INNER JOIN DB2.Table2 gp ON g.F1 = gp.F1 
WHERE (gp.f2 = 452677825) AND 
(g.f2 = 'A string value') LIMIT 0,56 

Diese Abfrage ist manchmal sehr schnell (< 1s) aber den String-Wert zu ändern, die g.F2 zu führt zu Anfragen verglichen wird, die nimmt sogar mehr als 11 und manchmal sogar 30 Sekunden. Ich kann nicht verstehen, warum das so ist. Das Folgende ist die Ausgabe des EXPLAIN für das SELECT, das ausgeführt wird.

1, 'SIMPLE', 'g', 'ref', 'PRIMARY,IX1', 'IX1', '17', 'const', 901, 'Using where' 
1, 'SIMPLE', 'gp', 'ref', 'IX3,IX2', 'IX2', '8', 'DB1.g.F1,const', 1, 'Using index' 

was scheint ein ziemlich guter Ausführungsplan zu sein. Die Anzahl der Zeilen in der obersten Zeile des Explain geht höchstens auf 2000, aber ich verstehe nicht, warum dies nicht länger als einen Sekundenbruchteil dauern sollte, um Ergebnisse zurückzugeben. Ich habe auch Profiler auf die Abfrage ausgeführt und festgestellt, dass die Abfragen verbringen 99.9% der Zeit in der Phase "Daten senden". Kann jemand bitte erklären, warum das so ist, und was kann getan werden, um die Abfrage zu optimieren?

Vielen Dank im Voraus, Tim

+0

Sind die Abfragen, die langsamer ausgeführt werden, auch die, die mehr Daten zurückgeben? –

+0

Hallo Will. Vielen Dank für Ihren Kommentar. Die Abfragen werden alle maximal 56 Zeilen zurückgeben, während ich die Anweisung begrenze. In der Regel gilt jedoch, je mehr Zeilen in der obersten Zeile von EXPLAIN stehen, desto länger dauert es. Dies ist jedoch nicht immer der Fall. – Tim

+0

Hat die Erhöhung der Anzahl der Zeichen, die von f2 in IX1 in Tabelle1 enthalten sind, irgendwelche Auswirkungen auf die Leistung? Konkret, erhöht dies auf 40 Dinge überhaupt verbessern? –

Antwort

1

Ich bin kein Experte auf diesem Gebiet, aber hier sind ein paar Gedanken:

Geschwindigkeit Abfrage dauert länger, wenn g.F2 Änderungen wegen Caching ist. MySQL speichert die Ergebnisse für jede Abfrage (bis der Cache voll ist), aber neue Abfragen werden in einem leeren Cache ausgeführt, so dass sie länger dauern. Sie sollten nicht darauf basierend optimieren. (Siehe How to measure accurately)

ich nicht von der Information, ob die g oder gp Tabelle höhere Spezifität hat sagen kann (scheint wie gp?) In der where Klausel, aber Sie können eine Unterabfrage statt versuchen. (Siehe How to force the inner query to execute first)

In Bezug auf Profilierung, ist es möglich, dass Sie eine physische Schwelle treffen sind, wie RAM-Zuweisung von mehr als (swap für die Leistung katastrophal ist), die nicht offensichtlich ist aus explain oder ob explain ist einfach falsch in diesem Fall.

+0

Hallo, Vielen Dank für Ihren Kommentar. Im Moment habe ich die obige Abfrage so geändert, dass sie IN anstelle eines Joins verwendet, wie Sie vorgeschlagen haben. Die Abfrage sieht nun folgendermaßen aus: SELECT g.F1 FROM (SELECT g.F1 FROM DB1.Tabelle1 g WHERE (g.f2 = 'abc')) ALS EINE WHERE A.F1 IN (SELECT gp.F1 VON DB2. Tabelle2 gp WHERE (gp.f2 = 452677825)) LIMIT 0,56 und die Abfrage läuft viel schneller (~ 1s, 2s max). Meine Mission ist es zu versuchen, dies noch weiter zu reduzieren! – Tim

0

Wenn Sie in der Lage sind, können Sie versuchen, Ihre my.cnf zu optimieren, die Eigenschaft, mit der Sie spielen möchten, ist key_buffer_size. MyISAM-Indizes werden in .MYI-Dateien gespeichert, wenn Sie diese finden und die Dateigrößen zusammenfassen (z. B. ls -lh /var/lib/mysql/dbname/*.MYI). Sie können grob abschätzen, wie groß der Schlüsselpuffer sein muss, um zu passen alle Ihre Indizes in. Die MySQL-Dokumente empfehlen jedoch, nicht mehr als 25% des Systemspeichers zu überschreiten.

0

Die Beziehung zwischen den beiden Tabellen ist, dass für Table1.F1 kann es bis zu ca. sein. 100 Zeilen in Table2.F1

Um zu klären, ist die Beziehung zwischen Table1.F1 und Table2.F1 Eins-zu-eins oder eins zu viele? Für mich impliziert diese Aussage Eins-zu-Viele, aber aus dem Schema sind jedes der Felder primäre (d. H. Eindeutige) Schlüssel.

Auf jeden Fall vermute ich, dass die Uniform g.f2(15) nicht einheitlich ist, und dass, wenn die statistischen Ausreißer getroffen werden, die Leistung entsprechend verschlechtert.

Sie die Ergebnisse der

SELECT f2(15) AS f2_15, COUNT(*) AS cnt 
FROM Table1 
GROUP BY f2(15) 
ORDER BY cnt DESC 

einige signifikante Ausreißer zeigen?