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 script Python simple qui peut être utilisé pour remplir une base de données de test avec des exemples de données s'ils souhaitent 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 |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
- EDIT SEPT 30: je peux supprimer la
WHERE
clause de cette requête, mais leEXPLAIN
reste toujours le même et la requête est toujours lente.
Une solution potentielle consiste à forcer un index. L'exécution de la même requête avec des FORCE INDEX (base_articel_date_published)
résultats dans une requête qui s'exécute dans 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
il ne peut plus être le meilleur index à utiliser.
Quelqu'un peut-il suggérer une stratégie pour améliorer les performances de cette requête?
base_article_is_published
(is_published
) .. me semble que c'est un type booléen ..Réponses:
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 deuxième sqlfriddle pour une explication sur la suppression du besoin
voir http://sqlfiddle.com/#!2/302710/2
Fonctionne assez 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 votre expliqué, une table de livraison INNER JOIN peut donner de meilleures performances comme les requêtes dans la réponse ci-dessous
/programming/18738483/mysql-slow-query-using-filesort/18774937#18774937
la source
JOIN
seulement mais MySQL ne récupérait pas d'index. Merci beaucoup RaymondRÉFLÉCHIR LA REQUÊTE
ou
MODIFIEZ VOS INDICES
ESSAIE !!!
la source
LIMIT 30
est dans la sous-requête (toutes ces 30 lignes ne seront pas également dans lemag_articles
tableau). Si je déplace leLIMIT
vers la requête externe, les performances sont les mêmes que dans mon original. Modifier les index: MySQL n'utilise pas non plus cet index. La suppression de laWHERE
clause de ma requête d'origine ne semble pas faire de différence.