3

Ich habe die folgenden zwei Tabellen in MySQL (vereinfacht).MySQL Abfrage mit JOIN nicht mit INDEX

  • clicks (InnoDB)
    • enthält rund etwa 70.000.000 Aufzeichnungen
    • einen Index für die Spalte date_added Hat
    • enthält eine Spalte link_id die
    im links Tabelle einen Datensatz bezieht sich
  • links (MyISAM)
    • enthält weit weniger Datensätze, um etwa 65.000

Ich versuche, einige analytische Abfragen mit diesen Tabellen auszuführen. Ich muss einige Daten herausnehmen, über Klicks, die innerhalb von zwei angegebenen Daten auftraten, während ich einige andere vom Benutzer ausgewählte Filter mit anderen Tabellen anwendete und sie in die Verknüpfungstabelle einfügte.

Meine Frage dreht sich jedoch um die Verwendung von Indizes. Wenn ich die folgende Abfrage ausführen:

SELECT 
    COUNT(1) 
FROM 
    clicks 
WHERE 
    date_added >= '2016-11-01 00:00:00' 
AND date_added <= '2016-11-03 23:59:59'; 

Ich bekomme eine Antwort zurück in 1.40 sek. Using EXPLAIN Ich finde, dass MySQL den Index auf der Spalte date_added wie erwartet verwendet.

EXPLAIN SELECT COUNT(1) FROM clicks WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-11-16 23:59:59'; 
+----+-------------+--------+-------+---------------+------------+---------+------+---------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+--------+-------+---------------+------------+---------+------+---------+--------------------------+ 
| 1 | SIMPLE  | clicks | range | date_added | date_added | 4  | NULL | 1559288 | Using where; Using index | 
+----+-------------+--------+-------+---------------+------------+---------+------+---------+--------------------------+ 

Allerdings, wenn ich LEFT JOIN in meinem links Tisch finde ich, dass die Abfrage dauert viel länger auszuführen:

SELECT 
    COUNT(1) AS clicks 
FROM 
    clicks AS c 
LEFT JOIN links AS l ON l.id = c.link_id 
WHERE 
    c.date_added >= '2016-11-01 00:00:00' 
AND c.date_added <= '2016-11-16 23:59:59'; 

, die in 6,50 sec abgeschlossen. Mit EXPLAIN finde ich, dass der Index nicht auf der date_added Spalte verwendet:

EXPLAIN SELECT COUNT(1) AS clicks FROM clicks AS c LEFT JOIN links AS l ON l.id = c.link_id WHERE c.date_added >= '2016-11-01 00:00:00' AND c.date_added <= '2016-11-16 23:59:59'; 
+----+-------------+-------+--------+---------------+------------+---------+---------------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra  | 
+----+-------------+-------+--------+---------------+------------+---------+---------------+---------+-------------+ 
| 1 | SIMPLE  | c  | range | date_added | date_added | 4  | NULL   | 6613278 | Using where | 
| 1 | SIMPLE  | l  | eq_ref | PRIMARY  | PRIMARY | 4  | c.link_id  |  1 | Using index | 
+----+-------------+-------+--------+---------------+------------+---------+---------------+---------+-------------+ 

Wie Sie den Index können nicht sehen, für die date_added Spalt in dem größeren Tisch und scheint weit mehr zu nehmen verwendet. Das scheint noch schlimmer zu werden, wenn ich mich an anderen Tischen beteilige.

Weiß jemand, warum dies passiert oder ob ich irgendetwas tun kann, um den Index für die Spalte date_added in der Clicks-Tabelle zu verwenden?


bearbeiten

Ich habe versucht, nur meine Statistiken, um aus der Datenbank eine andere Methode verwenden. Der erste Schritt in meiner Methode besteht darin, einen eindeutigen Satz von link_id aus der Clicks-Tabelle herauszuziehen. Ich habe festgestellt, dass ich wieder das gleiche Problem sehe, ohne JOIN. Der Index wird nicht benutzt wird:

Meine Frage:

SELECT 
    DISTINCT(link_id) AS link_id 
FROM 
    clicks 
WHERE 
    date_added >= '2016-11-01 00:00:00' 
AND date_added <= '2016-12-05 10:16:00' 

Diese Abfrage dauerte fast eine Minute.Ich lief auf das eine EXPLAIN und festgestellt, dass die Abfrage nicht den Index verwendet, wie ich es zu erwarten:

+----+-------------+---------+-------+---------------+----------+---------+------+----------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows  | Extra  | 
+----+-------------+---------+-------+---------------+----------+---------+------+----------+-------------+ 
| 1 | SIMPLE  | clicks | index | date_added | link_id | 4  | NULL | 79786609 | Using where | 
+----+-------------+---------+-------+---------------+----------+---------+------+----------+-------------+ 

ich erwartet, dass es den Index auf date_added verwenden würde das Ergebnis auf Filter nach unten und ziehen Sie dann die verschiedene link_id Werte. Irgendeine Idee, warum das passiert? Ich habe einen Index auf link_id sowie date_added.

+0

Fügen Sie ** index ** on link_id in den Clicks-Tabellen hinzu, die dabei helfen können, die Abfrageausgabezeit zu reduzieren –

+0

@SumanEStatic - 'INDEX (link_id)' würde _nicht_ helfen. –

+0

Riecht wie Sie MyISAM verwenden. Bitte geben Sie 'SHOW CREATE TABLE' an. –

Antwort

1

nicht absolut sicher, aber betrachten den Zustand von WHERE Zustand zu JOIN ON Zustand bewegt, da Sie eine äußere Verknüpfung durchführen (LEFT JOIN) es Unterschied in der Leistung im Gegensatz zu inner join macht, wo der Zustand es auf where oder join on Klausel entspricht.

SELECT COUNT(1) AS clicks 
FROM clicks AS c 
LEFT JOIN links AS l ON l.id = c.link_id 
AND (c.date_added >= '2016-11-01 00:00:00' 
AND c.date_added <= '2016-11-16 23:59:59'); 
+0

Danke für Ihre Antwort. Ich habe versucht, die Bedingungen von der WHERE-Klausel in den JOIN ON zu verschieben, wie vorgeschlagen, aber ich sehe immer noch das gleiche Problem. – Jonathon

+0

Verwenden Sie 'ON', um zu sagen, wie die Tabellen verwandt sind; Verwende 'WHERE' zum Filtern. Der Optimierer _kann sie identisch behandeln. Sie können das von 'EXPLAIN EXTENDED SELECT ... sehen; WARNHINWEISE ANZEIGEN; ' –

+0

@Rahul Ich habe meine Frage mit etwas anderem bearbeitet, das ich gefunden habe, das möglicherweise helfen könnte, das Problem zu diagnostizieren. Danke :) – Jonathon

1

Haben Sie einen gewöhnlichen JOIN anstelle des LEFT JOIN verwenden? LEFT JOIN behält alle Zeilen auf der rechten Seite bei, sodass derselbe Wert COUNT() als nicht verbundene Tabelle angezeigt wird. Wenn Sie nur die Zeilen aus Ihrer rechten Tabelle zählen möchten, die übereinstimmende Zeilen in der linken Tabelle enthalten, verwenden Sie JOIN, nicht LEFT JOIN.

Versuchen Sie, Ihren Index auf date_added fallen zu lassen und ersetzen Sie ihn durch einen zusammengesetzten Index auf (date_added, link_id). This sort of index is called a covering inde x. Wenn der Abfrageplaner weiß, dass er alles, was er von einem Index benötigt, abrufen kann, muss er nicht zurück in die Tabelle springen. In diesem Fall kann der Abfrageplaner den Index auf den Anfang Ihres Datumsbereichs zugreifen und dann eine index range scan bis zum Ende des Bereichs ausführen. Es muss sich aber immer noch auf die andere Tabelle beziehen.

(Bearbeiten) Versuchen Sie einen kleineren Datumsbereich aus Gründen des Experimentierens. Sehen Sie, wenn sich EXPLAIN ändert. In diesem Fall schätzt der Abfrageplaner möglicherweise die Kardinalität der Spalte "date_added" falsch.

Sie könnten einen index hint versuchen. Zum Beispiel versuchen

SELECT COUNT(1) AS clicks 
    FROM clicks AS c USE INDEX (date_added) 
    LEFT JOIN links AS l ON l.id = c.link_id 
WHERE etc 

Aber von Ihrem EXPLAIN Ausgang zu urteilen, sind Sie bereits eine Reihe Scan date_added tun. Ihr nächster Schritt, ob es Ihnen gefällt oder nicht, ist der Index der zusammengesetzten Deckung.

Stellen Sie sicher, ein Index für links(id) gibt. Es ist wahrscheinlich, weil es wahrscheinlich die PK ist.

Versuchen Sie es mit COUNT(*) statt COUNT(1). Es wird wahrscheinlich keinen Unterschied machen, aber es ist einen Versuch wert. COUNT(*) zählt einfach Zeilen, anstatt für jede Zeile, die gezählt wird, etwas auszuwerten.

(Nitpick) Ihr Datumsbereich riecht komisch. Verwenden Sie < für das Ende Ihrer Reichweite für die besten Ergebnisse, so.

WHERE c.date_added >= '2016-11-01' 
    AND c.date_added < '2016-11-17'; 

bearbeiten: Schauen Sie, die Abfrage Planer MySQL verwendet viele interne Wissen darüber, wie Tabellen strukturiert sind. Und, es kann nur einen Index pro Tabelle verwenden, um eine Abfrage als Ende 2016 zu erfüllen. Das ist eine Einschränkung.

SELECT DISTINCT column ist eigentlich eine ziemlich komplexe Abfrage, denn es muss die column in Frage gestellt werden. Wenn für diese Spalte ein Index vorhanden ist, wird sie wahrscheinlich vom Abfrageplaner verwendet. Wenn Sie diesen Index auswählen, können Sie keinen anderen Index auswählen.

Zusammengesetzte Indizes (abdeckende Indizes) manchmal , aber nicht immer lösen diese Art von Index-Auswahl Dilemma, und erlauben Index Dual-Verwendung. Sie können über all dies unter http://use-the-index-luke.com/

lesen Aber wenn Ihre betrieblichen Einschränkungen das Hinzufügen von zusammengesetzten Indizes verhindern, müssen Sie mit der Ein-Sekunden-Abfrage leben. Es ist nicht so schlimm.

Natürlich, sagen Sie nicht zusammengesetzten Indizes hinzufügen können Sie Ihre Arbeit zu erledigen ist wie folgt:

A: stuff fällt meinen LKW auf der Autobahn ab.

B: Legen Sie eine Plane über das Zeug und binden Sie es fest.

A: mein Chef lässt mich nicht eine Plane auf den LKW legen.

B: Nun, fahren Sie langsam.

+0

Danke für deine Antwort. Ich habe versucht, 'JOIN' anstelle von' LEFT JOIN' zu verwenden, ohne Erfolg, ich habe auch 'COUNT (*)' vergeblich versucht. Ich kann bestätigen, dass 'Links (ID)' ein Primärschlüssel ist und die Spalte 'clicks (link_id)' ebenfalls indiziert ist. Ich bin im Moment widerwillig, Änderungen an der Tabelle aufgrund ihrer Größe vorzunehmen. Daher war es mir nicht möglich, den Index zu löschen und einen Deckungsindex wie vorgeschlagen hinzuzufügen. Danke nochmal! – Jonathon

+0

Sie können den neuen Index hinzufügen, ohne zuerst den vorhandenen zu löschen. –

+0

Mit 'ALTER TABLE' können Sie beliebig viele Indizes gleichzeitig hinzufügen und löschen. –