So optimieren Sie Indizes für MySQL-Abfragen mit verschiedenen Sortierungen

7

Ich habe eine INNODB-Tabelle levels:

+ -------------------- + -------------- + ------ + ----- + --------- + ------- +
| Feld | Geben Sie | ein Null | Schlüssel | Standard | Extra |
+ -------------------- + -------------- + ------ + ----- + --------- + ------- +
| id | int (9) | NEIN | PRI | NULL | |
| Levelname | varchar (20) | NEIN | | NULL | |
| user_id | int (10) | NEIN | | NULL | |
| Benutzername | varchar (45) | NEIN | | NULL | |
| Bewertung | dezimal (5,4) | NEIN | | 0,0000 | |
| Stimmen | int (5) | NEIN | | 0 | |
| spielt | int (5) | NEIN | | 0 | |
| date_published | Datum | NEIN | MUL | NULL | |
| user_comment | varchar (255) | NEIN | | NULL | |
| spielbarer_Zeichen | int (2) | NEIN | | 1 | |
| is_featured | tinyint (1) | NEIN | MUL | 0 | |
+ -------------------- + -------------- + ------ + ----- + --------- + ------- +

Es gibt ~ 4 Millionen Zeilen. Aufgrund der Front-End-Funktionalität muss ich diese Tabelle mit einer Vielzahl von Filtern und Sortierungen abfragen. Sie sind auf playable_character, rating, plays, und date_published. Sie date_publishedkönnen nach dem letzten Tag, der letzten Woche, dem letzten Monat oder jederzeit (in den letzten 3 Jahren) gefiltert werden. Es gibt auch Paging. Abhängig von den Benutzeroptionen können die Abfragen beispielsweise wie folgt aussehen:

SELECT * FROM levels
WHERE playable_character = 0 AND
    date_published BETWEEN date_sub(now(), INTERVAL 3 YEAR) AND now()
ORDER BY date_published DESC
LIMIT 0, 1000;

SELECT * FROM levels
WHERE playable_character = 4 AND
    date_published BETWEEN date_sub(now(), INTERVAL 1 WEEK) AND now()
ORDER BY rating DESC
LIMIT 4000, 1000;

SELECT * FROM levels
WHERE playable_character = 5 AND
    date_published BETWEEN date_sub(now(), INTERVAL 1 MONTH) AND now()
ORDER BY plays DESC
LIMIT 1000, 1000;

Ich sollte das hinzufügen ratingund werde playsimmer als abgefragt DESC. Nur date_publishedkann entweder DESCoder sein ASC.

Ich habe mit einem Index begonnen idx_date_char(date_published, playable_character), der bei der ersten Beispielabfrage hier hervorragend funktioniert hat. Basierend auf einigen anderen Antworten habe ich zu zwei anderen Indizes gewechselt (date_published, playable_character, play) und (date_published, playable_character, Bewertung).

Die erste Abfrage wird immer noch sehr schnell ausgeführt. In EXPLAIN passieren jedoch einige ungewöhnliche Dinge, wenn player_character = x eine bestimmte Anzahl von Zeilen (~ 700.000) überschreitet: In EXPLAIN wird USING WHERE angezeigt.

Die erste Frage ist also, ob Verbesserungen an der Abfrage oder den Indizes möglich sind, und zweitens, welche MySQL-Einstellungen geändert werden sollten, um die großen Ergebnismengen zu berücksichtigen.

Anregungen sehr geschätzt. TIA.

Hal50000
quelle
Ich würde sicher gerne die Ausgabe "Erklärt" und "Indizes von Ebenen anzeigen" sehen, wenn ungewöhnliche Dinge passieren. Es sieht so aus, als würde das tiefe Scannen (größerer Startwert in LIMIT) die Verwendung einer temporären Tabelle und die Verwendung von Dateisortierung auslösen.
Raymond Nijland
Wie langsam laufen auch andere Abfragen? Wie viel Zeit wird benötigt, um die Ergebnisse zu erhalten?
Mindaugas Riauba
1
Ist das nicht ein Duplikat dieser Frage?: Stackoverflow.com/questions/19385610/…
ypercubeᵀᴹ
@ypercube sieht sicher aus wie ein Duplikat Ich dachte, ich hätte ein Déjà Vu, aber ich konnte das Thema auf Stackoverflow nicht mehr finden
Raymond Nijland
@ Hal50000 Wenn Ihre Erklärung "using filesort" anzeigt, denke ich, dass dies blogs.oracle.com/realneel/entry/… Ihr Problem sein könnte. Beachten Sie, dass der Artikel alt ist. ) siehe sqlfiddle.com/#!2/4f56a/2, ich denke, dass filesort dort 30 als Zeilen erhält. Ich denke auch, dass ich die Schätzung_rows_upper_bound () im Quellcode aufspüren muss, um eine bessere Erklärung zu erhalten.
Raymond Nijland

Antworten:

2
WHERE playable_character = 0 AND
    date_published BETWEEN date_sub(now(), INTERVAL 3 YEAR) AND now()

Beginnen Sie mit dem Element "=" und führen Sie dann den Bereich aus:

INDEX(playable_character, date_published);

"Paginierung", a la ORDER BY rating DESC LIMIT 4000, 1000;wird am besten gemacht, indem Sie sich daran erinnern, wo Sie "aufgehört" haben. Auf diese Weise müssen Sie nicht über die 4000 Datensätze scannen, die Sie nicht benötigen.

Rick James
quelle
-1

Erstellen Sie beide Indizes für eine bessere Leistung, wenn nicht viele Einfüge- oder Aktualisierungsabfragen erforderlich sind:

ADD INDEX (playable_character, date_published, rating desc)

ADD INDEX (playable_character, date_published, plays desc)

Andernfalls, wenn das Einfügen und Aktualisieren von Abfragen in dieser Tabelle häufiger verwendet wird als das Erstellen eines Index:

ADD INDEX (playable_character, date_published)

oder erstellen Sie keinen Index

Saddam Khan
quelle
-2

Allen hier fehlt ein Punkt - die fraglichen SQLs rufen 1000 Zeilen ab. Und 1000 Zeilen können nur dann schnell abgerufen werden, wenn die meisten Daten zwischengespeichert sind. Wenn die Daten nicht zwischengespeichert werden, müssen nacheinander 1000 zufällige Lesevorgänge durchgeführt werden, um die Daten abzurufen.

Guter festplattenbasierter Speicher mit schnellen Festplatten bietet bis zu ~ 200 Lesevorgänge pro Sekunde. Gängige Festplatten auch in RAID Ich bezweifle, dass sie sogar 100 verwalten. Das bedeutet mehr als 10 Sekunden, um selbst mit den besten Indizes Ergebnisse zu erzielen.

Auf längere Sicht funktionieren solche Datenmodelle und Abfragen also nicht. Jetzt werden die Abfragen schnell ausgeführt, wenn Sie auf zwischengespeicherte Daten klicken.

Mindaugas Riauba
quelle
Auch bei Deckungsindizes?
Ypercubeᵀᴹ
Dies ist eine der möglichen Ideen zur Reorganisation der Daten.
Mindaugas Riauba
Das Abdecken von Indizes liefert auch dann die besten Ergebnisse, wenn sie im Pufferpool zwischengespeichert werden. Andernfalls müssen Sie damit rechnen, auf die Festplatten-E / A zu warten.
Bill Karwin
Und für das, was es wert ist, werden SSD-basierte Systeme mit Tausenden von IOPS zum Mainstream für RDBMS-Server.
Bill Karwin
1
Es gibt viele Arten der guten / schlechten Indizierung - von einem Abdeckungsindex bis zu einem Tabellenscan. Dazwischen liegen optimaler nicht abdeckender Index, suboptimaler Index, Bereichsscan, unnötige Verknüpfungen usw. Das Abrufen von 1000 Zeilen kann bis zu mehr als 1000 Festplattentreffern (guter Index und gutes Caching) null E / A (guter Index und gutes Caching) erfordern ( riesiger Tisch, kein nützlicher Index).
Rick James