Ich habe zwei Tabellen, die erste Tabelle enthält alle Artikel / Blog-Beiträge innerhalb eines CMS. Einige dieser Artikel erscheinen möglicherweise auch in einer Zeitschrift. In diesem Fall haben sie eine Fremdschlüsselbeziehung zu einer anderen Tabelle, die magazinspezifische Informationen enthält.
Hier ist eine vereinfachte Version der Syntax zum Erstellen von Tabellen für diese beiden Tabellen, wobei einige nicht wesentliche Zeilen entfernt wurden:
CREATE TABLE `base_article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date_published` datetime DEFAULT NULL,
`title` varchar(255) NOT NULL,
`description` text,
`content` longtext,
`is_published` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `base_article_date_published` (`date_published`),
KEY `base_article_is_published` (`is_published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `mag_article` (
`basearticle_ptr_id` int(11) NOT NULL,
`issue_slug` varchar(8) DEFAULT NULL,
`rubric` varchar(75) DEFAULT NULL,
PRIMARY KEY (`basearticle_ptr_id`),
KEY `mag_article_issue_slug` (`issue_slug`),
CONSTRAINT `basearticle_ptr_id_refs_id` FOREIGN KEY (`basearticle_ptr_id`) REFERENCES `base_article` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Das CMS enthält insgesamt rund 250.000 Artikel. Ich habe ein einfaches Python-Skript geschrieben , mit dem eine Testdatenbank mit Beispieldaten gefüllt werden kann, wenn dieses Problem lokal repliziert werden soll.
Wenn ich aus einer dieser Tabellen auswähle, hat MySQL kein Problem damit, einen geeigneten Index auszuwählen oder Artikel schnell abzurufen. Wenn die beiden Tabellen jedoch in einer einfachen Abfrage zusammengefügt werden, z.
SELECT * FROM `base_article`
INNER JOIN `mag_article` ON (`mag_article`.`basearticle_ptr_id` = `base_article`.`id`)
WHERE is_published = 1
ORDER BY `base_article`.`date_published` DESC
LIMIT 30
MySQL kann keine geeigneten Abfragen auswählen und die Leistung sinkt. Hier ist die relevante Erklärung verlängert (die Ausführungszeit beträgt mehr als eine Sekunde):
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 1 | SIMPLE | mag_article | ALL | PRIMARY | NULL | NULL | NULL | 23830 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | base_article | eq_ref | PRIMARY,base_article_is_published | PRIMARY | 4 | my_test.mag_article.basearticle_ptr_id | 1 | 100.00 | Using where |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
- EDIT SEPT 30: Ich kann die
WHERE
Klausel aus dieser Abfrage entfernen , aber dieEXPLAIN
sieht immer noch gleich aus und die Abfrage ist immer noch langsam.
Eine mögliche Lösung besteht darin, einen Index zu erzwingen. Das Ausführen derselben Abfrage mit FORCE INDEX (base_articel_date_published)
Ergebnissen führt zu einer Abfrage, die in etwa 1,6 Millisekunden ausgeführt wird.
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 1 | SIMPLE | base_article | index | NULL | base_article_date_published | 9 | NULL | 30 | 833396.69 | Using where |
| 1 | SIMPLE | mag_article | eq_ref | PRIMARY | PRIMARY | 4 | my_test.base_article.id | 1 | 100.00 | |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
Ich würde es aus mehreren Gründen vorziehen, keinen Index für diese Abfrage erzwingen zu müssen, wenn ich dies vermeiden kann. Insbesondere kann diese grundlegende Abfrage auf verschiedene Arten gefiltert / geändert werden (z. B. durch Filtern nach issue_slug
). Danach ist sie base_article_date_published
möglicherweise nicht mehr der beste zu verwendende Index.
Kann jemand eine Strategie zur Verbesserung der Leistung für diese Abfrage vorschlagen?
base_article_is_published
(is_published
) wirklich löschen . Für mich ist es ein boolescher Typ.Antworten:
Was sollte damit geschehen, dass keine "temporäre Verwendung; Dateisortierung verwenden" erforderlich ist, da die Daten bereits in der richtigen Sortierung sind.
Sie müssen den Trick kennen, warum MySQL "Temporär verwenden; Dateisortierung verwenden" benötigt, um diese Notwendigkeit zu beseitigen.
In der zweiten sqlfriddle finden Sie eine Erklärung zum Entfernen der Notwendigkeit
siehe http://sqlfiddle.com/#!2/302710/2
Funktioniert ziemlich gut, ich brauchte dies auch vor einiger Zeit für Länder- / Stadttabellen. Siehe Demo hier mit Beispieldaten http://sqlfiddle.com/#!2/b34870/41
Bearbeitet möchten Sie diese Antwort möglicherweise auch analysieren, wenn base_article.is_published = 1 immer 1 Datensatz zurückgibt, wie in Ihrer Erklärung erläutert. Eine INNER JOIN-Auslieferungstabelle bietet möglicherweise eine bessere Leistung wie die Abfragen in der folgenden Antwort
/programming/18738483/mysql-slow-query-using-filesort/18774937#18774937
quelle
JOIN
nur verwendet, aber MySQL hat den Index nicht erfasst. Vielen Dank RaymondREFACTOR DIE FRAGE
oder
ÄNDERN SIE IHRE INDEXE
VERSUCHE ES !!!
quelle
LIMIT 30
leider nicht , da sich das in der Unterabfrage befindet (nicht alle dieser 30 Zeilen werden auch in dermag_articles
Tabelle enthalten sein). Wenn ich dieLIMIT
in die äußere Abfrage verschiebe, ist die Leistung dieselbe wie in meinem Original. Indizes ändern: MySQL verwendet diesen Index auch nicht. Das Entfernen derWHERE
Klausel aus meiner ursprünglichen Abfrage scheint keinen Unterschied zu machen.