2016-04-28 14 views
0

Ich habe eine Abfrage:Optimieren MySQL Query mit mehreren Indizes

SELECT items.* 
FROM items 
INNER JOIN other on other.some_key = items.some_key 
WHERE 
    items.partition_id = 7 AND 
    items.created_date > '2016-01-01 00:00:00' AND 
    (
    other.name like '%user query%' OR 
    match (fulltext_candidate) against ('+user* +query*' IN BOOLEAN MODE) OR 
    items.some_varchar like '%user query%' 
) 
ORDER BY items.created_date 
LIMIT 20 OFFSET 0; 

ich mehrere Indizes

ALTER TABLE `items` ADD INDEX `partition_id` (`partition_id`) 
ALTER TABLE `items` ADD FULLTEXT `fti` (`fulltext_candidate`) 
ALTER TABLE `items` ADD INDEX `created_date` (`created_date`) 
ALTER TABLE `items` ADD INDEX `some_varchar` (`some_varchar`) 
ALTER TABLE `other` ADD INDEX `name` (`name`) 

Die Abfrage hinzugefügt ist langsam. Das langsame Abfrageprotokoll zeigt die Abfrage, die alle Zeilen der Elementtabelle durchsucht. Ich glaube, dass MySQL nur einen Index für die Abfrage verwendet. Wenn ich eine reduzierte Abfrage mache, wo ich nur eine Spalte in die WHERE-Klausel einbeziehe, ist die Abfrage sehr schnell.

Wie kann ich einen einzelnen Index erstellen, der diese Abfrage optimiert und sowohl Volltexte als auch normale Indizes verwendet?

+0

Wie langsam ist langsam? Welche Art von Tisch liegst du? Wie lange dauert die Abfrage? –

+0

Eine Abfrage kann über eine Minute dauern. Die Tabelle ist InnoDB. – Dave

Antwort

0

Dies ist zu groß für einen Kommentar.

Das sieht seltsam aus.

inner join other on other.some_key = items.some_key 

Ich nehme an, some_key ist indiziert, aber hat einen sehr seltsamen Namen? Wenn dies der Fall ist und es nicht wie angegeben auf einen Index für die Abfrage trifft, würde ich wahrscheinlich einen vollständigen Tabellenscan durchführen, da dies der schnellste Weg ist, um die Daten zu erhalten. Ich müsste mehr über deine Daten wissen, um zu helfen. Als Beispiel. In kleinen Tabellen kann die Datenbank die gesamte Tabelle einlesen, da es sich um einige Kilobyte Daten handelt und Sie in Ihrer Abfrage auf mehrere Arten darauf zugreifen können. Der Optimierer betrachtet einen Index und ruft an, um wie viel er das Ergebnis filtern kann. Wenn diese Zahl niedrig ist, wird sie nicht verwendet.

+0

Ja, der Schlüssel zum Verbinden der Tabellen ist indiziert, das war hier nicht klar. – Dave

0

Ich nehme an, Sie haben einige Details aus der Abfrage entfernt - "other.some_key" und "items.somekey" (Ihre Join-Spalten) sollten unbedingt indiziert werden.

Versionen von MySQL vor 5.0 können nur einen einzelnen Index pro Abfrage verwenden. Ein explain wird Ihnen sagen, was in diesem Fall vor sich geht.

Es gibt ein paar Kriterien in Ihrer Abfrage, die keine nicht einen Index verwenden, ganz gleich, was Sie tun können:

other.name like '%user query%' 
items.some_varchar like '%user query%' 

haben eine führende Platzhalter, und der Index wird dir nicht gut tun (können Sie konvertieren stattdessen die Textsuche freischalten?). Es ist nicht klar, wie viel von einem Leistungseinbruch verursacht wird - wenn der Rest Ihrer Abfrage die Anzahl der übereinstimmenden Datensätze auf Hunderte oder Tausende herunterfiltert, macht dies wahrscheinlich keinen Unterschied. Auch hier wird EXPLAIN helfen.

Wenn Sie den führenden Platzhalter entfernen können und "some_varchar" zum zusammengesetzten Index hinzufügen können, sollte es helfen.

Für Ihre Abfrage würde ich einen einzigen zusammengesetzten Index erstellen; Auf diese Weise schätzt der Optimierer Sie nicht.

ALTER TABLE `items` ADD INDEX `compound` (some_key, partition_id, created_date) 
+0

Sie haben den Index für other.some_key und items.somekey korrekt. Ich habe keine Option, jetzt auf MySQL 5.0 zu aktualisieren Für die beiden Kriterien in der Abfrage, die LIKE verwenden, wenn ich den führenden Wildcard entfernen, wird die Leistung erhöhen? Wenn ich other.name und items.some_varchar umwandeln kann, um einen Volltextindex zu verwenden, kann oder soll ich sie zum zusammengesetzten Index hinzufügen? Kann ein zusammengesetzter Index FULLTEXT-Indices in einigen Spalten enthalten? Wenn nicht, und MySQL kann nur einen Index verwenden, muss ich zwischen einem zusammengesetzten Index und einem Volltext wählen? – Dave

+0

Ich habe die Antwort aktualisiert, um zu klären - bitte poste ein EXPLAIN, wenn du mehr Details willst. Bitte versuchen Sie auch den zusammengesetzten Index, den ich vorgeschlagen habe. –

+0

Vielen Dank für Ihre aktualisierte Antwort. Ich erkannte, dass ich tatsächlich auf MySQL 5.x bin. Ändert dies möglicherweise Ihren Vorschlag? – Dave

0

Antwort

haben diese;

items: INDEX(partition_id, created_date) 
other: INDEX(some_key) 

Kommentare auf alle anderen Chatter:

Die ORs verhindern jegliche Verwendung von Indizes (wie Ihre FULLTEXT) der Spalten keine andere werden für Ihre Abfrage (oder jede diskutierte Variante) nützlich sein in ihnen erwähnt.Das heißt, das Entfernen der führenden % wird immer noch nicht gut - wegen der OR.

(some_key, partition_id, created_date) macht es unbrauchbar für die WHERE und ORDER BY.

Auch wenn Sie den "Index Merge" verwenden könnten, wird es so selten aufgerufen, dass es nicht in Betracht gezogen wird. (Und es ist nur ein grober Bandaid.)

Wenn Sie einen MATCH ... haben, der einen FULLTEXT Index verwenden könnte, wird nur der FULLTEXT Index verwendet. Das heißt, wenn es verwendet werden kann. Auch dies verhindert die OR für Ihren Fall. Sicher, "zusammengesetzte" Indizes sind sehr nützlich; aber der Optimierer geht immer davon aus, dass FULLTEXT nützlicher ist.

Da FULLTEXT nicht verwendet werden kann, sollte das Optimierungsprogramm auf INDEX(partition_id, created_date) gehen und es nützlich finden.