J'ai deux tableaux, le premier tableau contient tous les articles/articles de blog dans un CMS. Certains de ces articles peuvent également apparaître dans un magazine, auquel cas ils ont une relation de clé étrangère avec une autre table contenant des informations spécifiques au magazine.
Voici une version simplifiée de la syntaxe de création de table pour ces deux tables avec quelques lignes non essentielles supprimées:
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;
Le CMS contient environ 250 000 articles au total et j'ai écrit un simple script Python qui peut être utilisé pour remplir une base de données de test avec des exemples de données s'ils veulent reproduire ce problème localement.
Si je sélectionne dans l'une de ces tables, MySQL n'a aucun problème à choisir un index approprié ou à récupérer des articles rapidement. Cependant, lorsque les deux tables sont réunies dans une requête simple telle que:
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 ne parvient pas à choisir une requête appropriée et les chutes de performances. Voici l'explication pertinente étendue (dont le temps d'exécution est supérieur à une seconde):
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 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 |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
WHERE
de cette requête, mais la EXPLAIN
a toujours la même apparence et la requête est toujours lente.Une solution potentielle consiste à forcer un index. L'exécution de la même requête avec FORCE INDEX (base_articel_date_published)
entraîne une requête qui s'exécute en environ 1,6 millisecondes.
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 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 | |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
Je préférerais ne pas avoir à forcer un index sur cette requête si je peux l'éviter, pour plusieurs raisons. Plus particulièrement, cette requête de base peut être filtrée/modifiée de diverses manières (comme le filtrage par le issue_slug
) Après quoi base_article_date_published
Peut ne plus être le meilleur index à utiliser.
Quelqu'un peut-il suggérer une stratégie pour améliorer les performances de cette requête?
Qu'en est-il de cela cela devrait supprimer la nécessité d'un "Utilisation temporaire; Utilisation du tri de fichiers" parce que les données sont déjà dans le bon tri.
Vous devez savoir pourquoi MySQL a besoin de "Utiliser temporaire; Utiliser le tri de fichiers" pour supprimer ce besoin.
Voir le deuxième sqlfriddle pour une explication sur la suppression du besoin
SELECT
*
FROM base_article
STRAIGHT_JOIN
mag_article
ON
(mag_article.basearticle_ptr_id = base_article.id)
WHERE
base_article.is_published = 1
ORDER BY
base_article.date_published DESC
voir http://sqlfiddle.com/#!2/302710/2
Fonctionne très bien, j'en avais besoin il y a aussi quelque temps pour les tableaux de pays/villes voir la démo ici avec des exemples de données http://sqlfiddle.com/#!2/b34870/41
Modifié, vous pouvez également vouloir analyser cette réponse si base_article.is_published = 1 renvoie toujours 1 enregistrement comme l'explique votre explication, une table de distribution INNER JOIN peut donner de meilleures performances comme les requêtes dans la réponse ci-dessous
https://stackoverflow.com/questions/18738483/mysql-slow-query-using-filesort/18774937#18774937
SELECT * FROM
(SELECT * FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
INNER JOIN mag_article B
ON A.id = B.basearticle_ptr_id;
ou
SELECT B.*,C.* FROM
(SELECT id FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
LEFT JOIN base_article ON A.id = B.id
LEFT JOIN mag_article C ON B.id = C.basearticle_ptr_id;
ALTER TABLE base_article DROP INDEX base_article_is_published;
ALTER TABLE base_article ADD INDEX ispub_datepub_index (is_published,date_published);