2013-04-15 4 views
7

Ich bin ziemlich neu in MySQL und ich versuche, eine bestimmte Menge von Zeilen wählen mit dieser Anweisung:DISTINCT-Anweisung in MySQL SELECT nimmt 10 Minuten

SELECT DISTINCT sp.atcoCode, sp.name, sp.longitude, sp.latitude 
FROM `transportdata`.stoppoints as sp 
INNER JOIN `vehicledata`.gtfsstop_times as st ON sp.atcoCode = st.fk_atco_code 
INNER JOIN `vehicledata`.gtfstrips as trip ON st.trip_id = trip.trip_id 
INNER JOIN `vehicledata`.gtfsroutes as route ON trip.route_id = route.route_id 
INNER JOIN `vehicledata`.gtfsagencys as agency ON route.agency_id = agency.agency_id 
WHERE agency.agency_id IN (1,2,3,4); 

Allerdings ist die select-Anweisung nimmt um 10 Minuten, also ist etwas klar im Gange. Ein wichtiger Faktor ist, dass die Tabelle gtfsstop_times sehr groß ist. (~ 250 Millionen Datensätze)

Indizes scheinen ordnungsgemäß eingerichtet zu sein; Alle obigen Joins verwenden indizierte Spalten. Tischgrößen sind, etwa:

gtfsagencys - 4 rows 
gtfsroutes - 56,000 rows 
gtfstrips - 5,500,000 rows 
gtfsstop_times - 250,000,000 rows 
`transportdata`.stoppoints - 400,000 rows 

Der Server verfügt über 22 GB Speicher, ich habe den InnoDB-Pufferpool zu 8G gesetzt und ich bin mit MySQL 5.6.

Kann jemand einen Weg sehen, diesen Lauf schneller zu machen? Oder überhaupt!

Ist es wichtig, dass die Stoppoints-Tabelle in einem anderen Schema ist?

EDIT: EXPLAIN SELECT ... gibt diese:

enter image description here

+2

Wie funktioniert das, wenn Sie das Qualifikationsmerkmal "DISTINCT" weglassen? Was erhalten Sie, wenn Sie 'EXPLAIN' in der Abfrage verwenden? –

+3

Was ist der Erklärungsplan? Fügen Sie es in Pastebin oder einen Kern ein –

+1

Ich bin nicht sicher, wie ich das testen würde, da, wenn ich den Qualifier weglasse, ungefähr 250 Millionen Zeilen zurückgegeben werden. Tut mir leid, wenn das Unsinn erscheint, bin ich ein wenig neu beim Testen/Debuggen von Abfragen. –

Antwort

6

Es sieht so aus, als ob Sie versuchen, basierend auf bestimmten Kriterien, eine Sammlung von Haltepunkten zu finden. Und Sie verwenden SELECT DISTINCT, um doppelte Stopppunkte zu vermeiden. Ist das richtig?

Offenbar ist atcoCode ein eindeutiger Schlüssel für Ihre Stoppoints-Tabelle. Ist das richtig?

Wenn ja, versuchen Sie dies:

SELECT sp.name, sp.longitude, sp.latitude, sp.atcoCode 
    FROM `transportdata`.stoppoints` AS sp 
    JOIN ( 
    SELECT DISTINCT st.fk_atco_code AS atcoCode 
     FROM `vehicledata`.gtfsroutes AS route 
     JOIN `vehicledata`.gtfstrips AS trip ON trip.route_id = route.route_id 
     JOIN `vehicledata`.gtfsstop_times AS st ON trip.trip_id = st.trip_id 
     WHERE route.agency_id BETWEEN 1 AND 4 
) ids ON sp.atcoCode = ids.atcoCode 

Dies hat ein paar Dinge: Es beseitigt eine Tabelle (Agentur), die Sie scheinen nicht zu brauchen. Es ändert die Suche nach Agentur-ID von IN(a,b,c) zu einer Bereichssuche, die helfen kann oder nicht. Und schließlich verschiebt es die Verarbeitung von einer Situation, in der es eine ganze Tonne von Daten zu einer Unterabfrage-Situation behandeln muss, wo es nur die ID-Werte behandeln muss.

(JOIN und INNER JOIN sind gleich. Ich benutzte JOIN die Abfrage ein bisschen leichter zu lesen.)

Dies sollte man ein wenig beschleunigen. Aber, es muss gesagt werden, ein Viertel Gigarow-Tisch ist ein großer Tisch.

+0

+1 für das Nachdenken über die tatsächliche SQL und das Aufspüren dieser Optimierungen. Ich wusste nicht einmal, dass Sie 'JOIN (SELECT ...)' als gültige Syntax verwenden könnten. Das hat meine Abfragezeiten halbiert, danke. Schauen Sie sich einfach die andere vorgeschlagene Antwort an. –

+0

Dies ist in der Tat eine signifikante Optimierung auf was ich hatte, danke für die tolle Antwort. –

+0

Übrigens, @Carlos P, lassen Sie alles, was Sie nicht brauchen, aus der Liste der Spalten, die Sie auswählen (SELECT). Verwenden Sie tatsächlich die Werte 'name' und' atcoCode' in Ihrer Anwendung? Wenn nicht, fragen Sie nicht nach ihnen: Diese Ergebnismenge ist ein Drittel eines Megarows oder so, und es braucht Zeit, um so viele Daten vom Server zum Client zu mischen. –

3

250M Aufzeichnungen zu haben, würde ich die gtfsstop_times Tabelle auf eine Spalte Scherbe. Anschließend kann jede Sharded-Tabelle in einer separaten Abfrage verknüpft werden, die in separaten Threads parallel ausgeführt werden kann. Sie müssen nur die Ergebnismengen zusammenführen.

+0

Kannst du in deiner Antwort ein bisschen mehr erklären, was du mit "sharding" meinst? Vielen Dank. – Jocelyn

+0

Er meint dies http://xeround.com/blog/2011/11/mysql-sharding-vs-mysql-partitioning und hier http://en.wikipedia.org/wiki/Shard_(database_architecture) –

+0

Aus Neugier, Wie würde das funktionieren? Wäre es nicht so zeitaufwendig, die Ergebnissätze zusammenzuführen, wie der ursprüngliche Job, da wir nach einzelnen Elementen suchen? –

2

Der Trick ist zu reduzieren, wie viele Zeilen von gtfsstop_times SQL ausgewertet werden muss. In diesem Fall wertet SQL zuerst jede Zeile im inneren Join von gtfsstop_times und transportdata .stoppoints, rechts? Wie viele Zeilen hat transportdata .stoppoints? Dann wertet SQL die WHERE-Klausel aus und wertet dann DISTINCT aus. Wie macht es DISTINCT? Indem Sie jede einzelne Zeile mehrmals betrachten, um festzustellen, ob es andere Zeilen gibt. Das würde ewig dauern, oder?

GROUP BY quetscht jedoch alle übereinstimmenden Zeilen schnell zusammen, ohne sie zu bewerten. Normalerweise verwende ich Joins, um die Anzahl der Zeilen schnell zu reduzieren, die die Abfrage auswerten muss. Dann schaue ich mir meine Gruppierung an.

In diesem Fall möchten Sie DISTINCT durch Gruppierung ersetzen.

Versuchen Sie dies;

SELECT sp.name, sp.longitude, sp.latitude, sp.atcoCode 

FROM `transportdata`.stoppoints as sp 
    INNER JOIN `vehicledata`.gtfsstop_times as st ON sp.atcoCode = st.fk_atco_code 
    INNER JOIN `vehicledata`.gtfstrips as trip ON st.trip_id = trip.trip_id 
    INNER JOIN `vehicledata`.gtfsroutes as route ON trip.route_id = route.route_id 
    INNER JOIN `vehicledata`.gtfsagencys as agency ON route.agency_id = agency.agency_id 

WHERE agency.agency_id IN (1,2,3,4) 

GROUP BY sp.name 
    , sp.longitude 
    , sp.latitude 
    , sp.atcoCode 
+0

Danke, ich habe das ausprobiert, aber es * hat * die Abfragezeit tatsächlich erhöht. Ich habe es mit einer viel kleineren Anfrage versucht ('WHERE Agentur.agency_id = 1') das dauert normalerweise 4-5 Sekunden und es dauerte ca. 8 Sekunden. 'sp.name',' sp.longitude', 'sp.latitude' sind nicht indiziert, könnte das der Grund sein? Ich bin mir nicht sicher, ob ich die Logik der Gruppierung nach allen vier dieser Spalten verstehe. Ist das notwendig und wenn ja, sollte ich sie alle indexieren? Ich bin besorgt, dass der Leistungseinbruch bei der Indexierung allesamt den Nutzen überwiegen könnte. –

+0

Ihrer Meinung nach ist diese Art und Weise es besser zu tun als @Ollie Jones Antwort, da sein Vorschlag scheint viel schneller durchzuführen. –

+0

Kann ich auch hinzufügen, dass ich einen Fehler in meiner Frage gemacht habe, die Spalte DISTINCT ist sp.atcoCode - dies könnte die Anomalie erklären? –

1

Dort andere wertvolle Antworten auf Ihre Frage und meins ist eine Ergänzung dazu. Ich nehme an, sp.atcoCode und st.fk_atco_code sind indizierte Spalten in ihrer Tabelle.

Wenn Sie überprüfen und sicherstellen können, dass Agentur-IDs in der WHERE-Klausel gültig sind, können Sie die Verknüpfung `vehicledata .gtfsagencys im JOINS-Objekt entfernen, da Sie keine Datensätze aus der Tabelle abrufen.

SELECT DISTINCT sp.atcoCode, sp.name, sp.longitude, sp.latitude 
FROM `transportdata`.stoppoints as sp 
INNER JOIN `vehicledata`.gtfsstop_times as st ON sp.atcoCode = st.fk_atco_code 
INNER JOIN `vehicledata`.gtfstrips as trip ON st.trip_id = trip.trip_id 
INNER JOIN `vehicledata`.gtfsroutes as route ON trip.route_id = route.route_id 
WHERE route.agency_id IN (1,2,3,4); 
+0

Danke, obwohl ich glaube @Ollie Jones hat das schon entdeckt? Schätze die Antwort jedoch. –