2012-04-13 11 views
2

Lesen this wiki article, fand ich heraus, dass die SELECT-Leistung getötet wird, wenn IN() -Klauseln mit indizierten Spalten in einer MySQL-Datenbank verwenden. Meine Frage ist, wie kann ich meine Abfrage neu schreiben, so dass sie keine IN() -Klausel verwendet, während sie ihre Funktionalität behält?Optimieren Sie eine Abfrage mit IN() -Klauseln

Meine Frage ist:

SELECT 
    `Route`.`route_id`, `Route`.`order`, `Route2`.`order` 
FROM 
    `routes` AS `Route` 
INNER JOIN 
    `routes` AS `Route2` 
ON `Route`.`route_id` = `Route2`.`route_id` 
WHERE 
    `Route`.`station_line_id` IN ([10 values]) AND 
    `Route2`.`station_line_id` IN ([10 values]) AND 
    `Route`.`order` <= `Route2`.`order` 
GROUP BY ` 
    `Route`.`station_line_id`, `Route2`.`station_line_id`, (`Route2`.`order` - `Route`.`order`) 

und ich habe alle Spalten indiziert (route_id, station_line_id, STATION_ID und line_id), mit der ID-Spalte erzeugt nur schreibgeschützt, wenn der Primärschlüssel (die Tabelle ist, also keine Sorge, alles zu indexieren). Die [10 values] in der IN() -Klausel sind Komma getrennt, wie: IN(1, 2, ..., 10).

Grundsätzlich schließe ich mich selbst der Tabelle Routen Tabelle und gruppieren die Ergebnisse, um die gewünschten Datensätze zu erhalten. Die anderen Verknüpfungen werden zum Abrufen zugehöriger Daten verwendet.

Leistung, mit InnoDB Speicher-Engine, führe ich eine ähnliche Abfrage in> 30 Sekunden. Mit MyISAM bekomme ich> 5 Sekunden. Aber ich glaube, Ergebnisse können noch schneller abgerufen werden. Ich habe ~ 4,5 Millionen Datensätze in der Tabelle.

+0

vorsichtig formatieren Sie Ihre Abfrage ein wenig? –

+0

Ich habe meine Frage bearbeitet, sorry. – linkyndy

+0

Sind das 10 Werte: 'IN (1,3,47, ... 89)' oder 'IN (SELECT Spalte FROM Tabelle)'? –

Antwort

1

Sie erhalten die beste Leistung in einer solchen Abfrage mit einem 'Hash-Index'. Der "Standard" -Index ist ein B + -Baum, mit dem Sie Einträge in der Log (n) -Zeit suchen können, wobei n die Anzahl der Zeilen in der Tabelle ist. Sie haben auch eine sortierte Reihenfolge, so dass Sie effizient Abfragen wie ... WHERE station_line_id > 14 ausführen können, was Sie in Ihrer Order Spalte verwenden möchten.

In Ihrem Fall jedoch, mit einer IN Klausel, suchen Sie nur nach Äquivalenz. In diesem Fall muss ein B + -Baum alle m Ihrer "[10 Werte]" separat suchen, was Ihnen m * log (n) Zeit kostet, was offensichtlich 5-30 Sekunden dauert.

Ein Hash-Index wird verwendet, um äquivalente Einträge in einer konstanten Zeit (sehr schnell) nachzuschlagen, die (theoretisch) nicht von der Anzahl der Zeilen in Ihrer Tabelle abhängt - sie wird immer sehr schnell sein, selbst bei großen Tabellen. Der Nachteil eines Hash-Index ist, dass Sie ihn nicht für Abfragen wie < oder > verwenden können, aber er ist der schnellste bei Äquivalenzabfragen, wie Sie sie in Ihrer IN-Klausel in station_line_id machen.

Bearbeiten: Für MySQL speziell unterstützen sie leider nicht HASH-Indizes auf einer ihrer populären Datenbank-Engines. Wenn Sie die MEMORY- oder HEAP-Engine verwenden können, können Sie einen HASH-Index verwenden - und wenn Sie alles im Speicher haben, wird sich die Leistung wahrscheinlich ziemlich verbessern. Einen Versuch wert.

+0

Ich bin derzeit auf einem freigegebenen Host und ich glaube, das Speichern solcher Datenmengen im Speicher ist keine Option (oder?). – linkyndy

+1

MyISAM und InnoDB haben keine Hash-Indizes. –

+0

Kann immer noch einen Versuch wert sein, abhängig von Ihrer Datenstruktur. Ich habe eine 45 Millionen Zeilen Tabelle, die in 2,7 GB Daten und 1,1 GB Index passt. Bei dieser Geschwindigkeit könnte Ihr Tisch nur etwa .27 + .11 GB <= 400 MB Speicher belegen. Ich weiß nicht, was Ihre Server-Anforderungen sind, aber 512 MB ist keine unangemessene Menge an Speicher für ein VPS zu haben. Nicht sicher, ob das eine Option für Sie ist, aber ich kann eine Leistungssteigerung garantieren. –