MySQL index für Spalten mit numerischen macht die Abfrage langsamer
Habe ich ein Optimierungs problem mit einem ziemlich großen Tabelle (~1,7 M Zeilen).
Gibt es zwei Spalten, bei der Auswahl von Zeilen, nennen wir Sie colA und colB. Sie sind beide vom Typ 'double' (5 Dezimalstellen) und reichen von:
colA: -90 ~ 90
colB: -180 ~ 180
Ohne einen index, jede Abfrage der form:
SELECT * FROM table where colA BETWEEEN a and b AND colB BETWEEN c and d
dauert ungefähr die gleiche Zeit zu laufen (~ 1 Sekunde), unabhängig von der Reichweite von (a,b) und (c,d) (als MySQL hat zu prüfen, jede Zeile).
Wenn ich einen index hinzuzufügen, colA und colB, passieren zwei Dinge: Abfragen, in denen der (a,b) & (c,d) - Reihe sind klein, zum Beispiel:
SELECT * FROM table where colA BETWEEEN -4 and 4 AND colB BETWEEN 3 and 7
laufen sehr schnell (~ 1/10 Sekunde). Jedoch ist die Ausführungszeit erhöht sich mit dem Bereich zwischen den abgefragten Werte. Zum Beispiel:
SELECT * FROM table where colA BETWEEEN -80 and 80 AND colB BETWEEN -150 and 150
dauert etwa eine minute, um Sie auszuführen.
Ich weiß, wie B-Bäume, Werke für Streicher, aber ich bin nicht sicher, dass der Mechanismus, wenn die Daten numerisch und die Abfrage erfolgt mit einer Palette.
Wenn jemand könnte vorschlagen, wie optimieren diese Abfrage wäre ich dankbar. Ein Gedanke ist zum verwenden des index für kleine Bereiche und teilen MySQL, es nicht zu verwenden für die größeren, aber ich konnte nicht finden, dass ein Befehl, der dies ermöglicht.
Dank
EDIT: das erklärt
Es ist etwas, was ich blöderweise vergessen habe zu erwähnen. Die Ergebnisse sind sortiert nach rand() - ich bin mir bewusst, wie ineffizient das ist, aber ich konnte sehen keinen anderen Weg, um eine begrenzte Anzahl von Zeilen aus der Tabelle nach dem Zufallsprinzip.
Hinzufügen von rand() hat keinen Einfluss auf die Ausführung der Zeit, wenn kein index vorhanden ist, aber drastisch und erhöht die Zeit, die genommen, wenn es ist.
EDIT2: dies ist die Verwendung von zusammengesetzten Indizes.
KLEINE AUSWAHL:
"explain select * from table where-colA zwischen 35 und 38 und colB zwischen -10 und 5 ORDER BY RAND() LIMIT 20"
9783 Zeilen
NO INDEX (schnell)
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | table | ALL | NULL | NULL | NULL | NULL | 1673784 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
MIT INDEX (sehr schnell)
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | table | range | test | test | 18 | NULL | 136222 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
GROßE AUSWAHL:
"explain select * from table where-colA zwischen -80 und 80 und colB zwischen -150 und 150 ORDER BY RAND() LIMIT 20;"
1631862 Zeilen
NO INDEX (schnell)
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | table | ALL | NULL | NULL | NULL | NULL | 1673784 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
MIT INDEX (sehr langsame: > 60 Sekunden)
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | table | ALL | test | NULL | NULL | NULL | 1673784 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
EDIT3:
Zusammenfassen:(alle Abfragen beschränkt auf Rückkehr 20 Zeilen)
große Auswahl mit rand() mit index: 45 Sekunden
große Auswahl, ohne rand(), mit index: 0.003 Sekunden
große Auswahl mit rand, kein index: 1 Sekunde
große Reichweite ohne rand, kein index: 0.003 Sekunden
Die Anomalie ist: "der große range mit rand() mit index, 45 Sekunden".
- Setzen
EXPLAIN
für beide Abfragen bitte. Auch du wirst selbst sehen, dass im zweiten Fall wird kein index verwendet wird - "Wenn ich einen index hinzuzufügen, colA und colB" --- klären Sie dies bitte. Für die Fragen, die Sie uns zeigte - den effizientesten wäre 2 getrennt - Indizes. Eine für
ColA
, und ein anderes fürColB
. - Warum sollten zwei getrennte Indizes effizienter sein, als eine composite-index? In den Abfragen angezeigt, die beiden Spalten in der where-Klausel, kombiniert mit UND, das scheint zu sein, das perfekte Szenario für einen zusammengesetzten index.
- falsch. zwei getrennte Indizes ist nicht gut, zu viele Duplikate.
- nur weil
BETWEEN
(sowie<
etc) kann optimiert werden für den Einsatz für die beiden wichtigen Teile. Lesen Sie überindex range
: dev.mysql.com/doc/refman/5.1/en/... - Ihr Satz, ich war falsch, ist falsch ;-P
- Ich hol mir das ERKLÄRT, ich muss die Indizes neu erstellen
- "Hinzufügen von rand() hat keinen Einfluss auf die Ausführung der Zeit, wenn kein index vorhanden ist, aber drastisch und erhöht die Zeit, die genommen, wenn es ist." Tut das entfernen von rand() verringern die Zeit genommen, wenn es einen index (und eine große Auswahl)?
- große Auswahl mit rand() mit index: 45 Sekunden ** große Auswahl, ohne rand(), mit index: 0.003 Sekunden ** ************** große Auswahl mit rand, kein index: 1 Sekunde ** große Auswahl, ohne rand, kein index: 0.003 Sekunden
- "große Auswahl, ohne rand(), mit index: 0.003 Sekunden" Können Sie überprüfen, dass dies immer noch mit der index?
- Sorry für die Formatierung oben. Es scheint, dass das hinzufügen von rand () - die Zeit erhöht sich drastisch, wenn es einen index (von 0,003 bis 45 Sekunden), aber nicht so, wenn kein index vorhanden ist (von 0,003 bis 1 Sekunde)
- lassen Sie mich sehen, es könnte zwischengespeichert haben, dass gerade jetzt. edit: Es wird über den index. Alle Abfragen werden mit "LIMIT 20'
- Nur realisiert es würde nicht Zwischenspeichern, die Abfragen, wie Sie sind bestellt, mit rand()
- Ich denke, die GRENZE 20 macht den Unterschied. Siehe meine aktualisierte Antwort. Für Lösungen, die wahrscheinlich nur löschen Sie den index, oder fügen Sie einen Hinweis für, wenn der Bereich groß ist.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Arbeiten Sie auf dieselbe Weise für zahlen wie für strings.
Die Laufzeit eines full-table-scan nicht deutlich variieren mit dem Inhalt der WHERE-Bedingung. Die Zeit, die für die index-Zugriff Weg ist proportional zu der Anzahl der zurückgegebenen Zeilen. Wenn eine Abfrage auswählt, die einen erheblichen Teil der Tabelle, mit einem index, wird immer langsamer sein, als es nicht über einen index.
Einen index-Zugriff Weg ist nur dann effizient, wenn der index-Selektivität ist ausreichend, d.h. die Anzahl der Zeilen, die abgerufen werden ist klein (einige sagen 10% höchstens). Die Ausführungszeit wird in etwa proportional zur Anzahl der Zeilen zurückgegeben, und konnte am Ende langsamer als ein full table scan.
Den Abfrage-Optimierer zu verwenden hat Statistiken und Heuristiken, um festzustellen, ob ein index verwendet werden soll. Vielleicht müssen Sie zur Aktualisierung dieser Statistik mit OPTIMIZE TABLE. Wenn es weiterhin fehlschlägt, um die richtige Entscheidung, können Sie helfen, es mit Hinweise.
Anderen Optionen könnte das löschen der index (wenn du siehst nie irgendeinen nutzen daraus, eine Konstante, eine Sekunde Reaktionszeit ist gut genug), oder zu versuchen, einen zusammengesetzten index auf beide Spalten (auch nur, wenn die Anzahl der Datensätze aus der Abfrage resultiert, ist kleiner).
Nun, dass Sie erwähnen, LIMIT 20, beginnt es macht mehr Sinn:
VERSCHACHTELTE SCHLEIFE mit vielen Ergebnissen + ART
Erhalten Sie ALLE Datensätze (in der Reihe) aus dem index, Holen Sie Sie eins nach dem anderen aus der Tabelle, dann Sortieren, dann limit auf 20
GESCHACHTELTE SCHLEIFE abgebrochen bei 20 Datensätze
Bekommen 20 Datensätze aus dem index, Holen Sie Sie eins nach dem anderen vom Tisch, und gibt Sie zurück. Keine Sortierung in der Tat keine große Auswahl.
FULL TABLE SCAN + ART
Lesen Sie die gesamte Tabelle, halten, was in Reichweite ist, dann Sortieren, dann limit auf 20
FULL TABLE SCAN, abgebrochen bei 20 Datensätze
Beginnen Sie mit dem Lesen der Tabelle, halten, was in Reichweite ist, zu stoppen, wenn Sie haben 20 und zurückgeben.
IGNORE INDEX
vor OP zeigt unsEXPLAINS
. Und erklärt, wir werden sehen, dass mysql bereits ignoriert die Indizes selbst, ich Wette.a
undb
vor 😉 er wollte nicht sagen, dass.Indizes mit vielen Duplikate sind eine Verschwendung.
Stellen Sie sicher, dass Ihre index verwendet beide Felder;
für colB Sie können weitere hinzufügen,
Grüße,
/t
Letzte Abfrage sollte nicht länger dauern als das erste. MySQL kann nicht über den index aktualisiert, siehe
OPTIMIZE TABLE
Außerdem können Sie prüfen, wie Sie beabsichtigt, die Abfrage mit
ERKLÄREN
undEXPLAIN ANALYZE
.Schließlich, Sie können erzwingen, dass der index deaktiviert werden, mit
IGNORE INDEX (idx_name)