Fermer2
ZephLe 15/06/2015 à 21:57
Hello,

j'ai un problème de performance avec une requête MySQL que je n'arrive pas à comprendre (pour info, c'est la cause des lenteurs actuelles de la page "mes sujets"). J'ai essayé de le reproduire avec les conditions les plus simples possibles, voilà ce que ça donne :SELECT member.id, member_cache.id, section.id, topic.id FROM topic INNER JOIN ( section INNER JOIN ( member LEFT JOIN (member_cache) ON member_cache.id = member.id ) ON member.id = section.last_member ) ON section.id = topic.section WHERE topic.last_time IS NOT NULL ORDER BY topic.last_time DESC LIMIT 0, 1
Schéma des tables utilisées dans cette requête
CREATE TABLE `member` ( `id` int(10) unsigned NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE `member_cache` ( `id` int(10) unsigned NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE `section` ( `id` int(10) unsigned NOT NULL, `last_member` int(10) unsigned NOT NULL DEFAULT '0' ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE `topic` ( `id` int(10) unsigned NOT NULL, `section` int(10) unsigned NOT NULL, `last_time` int(10) unsigned NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ALTER TABLE `member` ADD PRIMARY KEY (`id`); ALTER TABLE `member_cache` ADD PRIMARY KEY (`id`); ALTER TABLE `section` ADD PRIMARY KEY (`id`); ALTER TABLE `topic` ADD PRIMARY KEY (`id`), ADD KEY `section__last_time` (`section`,`last_time`), ADD KEY `last_time` (`last_time`);

C'est une requête qui est supposée sortir les N derniers sujets (en l'occurrence un seul) triés par date de dernière activité (table "topic"), ainsi que leur section parente (table "section"), le dernier posteur de chaque sujet (table "member") et quelques informations supplémentaires sur ce posteur (table "member_cache"). Si j'exécute cette requête avec un "EXPLAIN", j'obtiens ça :
idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEsectionALLPRIMARYNULLNULLNULL2188Using temporary; Using filesort
1SIMPLEmembereq_refPRIMARYPRIMARY4temporary.section.last_member1Using index
1SIMPLEmember_cacheeq_refPRIMARYPRIMARY4temporary.section.last_member1Using index
1SIMPLEtopicrefsection__last_time,last_timesection__last_time4temporary.section.id106Using index condition

Ce que je ne comprends pas c'est que MySQL se retrouve à scanner 2188 lignes de la table "section" (1ière ligne du tableau, valeurs en rouge) alors que la seule utilisation de cette table est faite pour récupérer la section parente de chaque sujet, et qu'on utilise pour ça son champ "id" qui se trouve être sa clé primaire. C'est d'ailleurs bien indiqué dans la première ligne, on voit comme "possible_keys" PRIMARY, mais au final aucune clé n'est utilisée pour une raison qui m'échappe et on se retrouve à scanner toute la table.

Pourquoi est-ce que MySQL décide de ne pas utiliser cet index ? C'est d'autant plus curieux que si je supprime la jointure la plus intérieure sur "member_cache" (qui a priori n'influe en rien sur la table "section"), alors l'index est correctement utilisé et la requête tourne beaucoup plus vite. Ça parle à quelqu'un ? À défaut, vous savez comment je pourrais avoir plus d'informations sur les raisons qui provoquent ce choix de MySQL ?

Si vous voulez reproduire en local, j'ai exporté la base et les données utilisées ici, et j'ai constaté le problème sur les versions 5.5.35-log et 5.6.15 de MySQL.