La requête suivante est assez simple. Il sélectionne les 20 derniers enregistrements d'une table de messages pour une utilisation dans un scénario de pagination. La première fois que cette requête est exécutée, cela prend de 15 à 30 secondes. Les exécutions suivantes prennent moins d'une seconde (je suppose que la mise en cache est impliquée). J'essaie de déterminer pourquoi la première fois prend tellement de temps.La requête simple prend 15-30 secondes
est ici la requête:
SELECT DISTINCT ID,List,`From`,Subject, UNIX_TIMESTAMP(MsgDate) AS FmtDate
FROM messages
WHERE List='general'
ORDER BY MsgDate
LIMIT 17290,20;
version MySQL: 4.0.26-log
Voici le tableau:
messages CREATE TABLE `messages` (
`ID` int(10) unsigned NOT NULL auto_increment,
`List` varchar(10) NOT NULL default '',
`MessageId` varchar(128) NOT NULL default '',
`From` varchar(128) NOT NULL default '',
`Subject` varchar(128) NOT NULL default '',
`MsgDate` datetime NOT NULL default '0000-00-00 00:00:00',
`TextBody` longtext NOT NULL,
`HtmlBody` longtext NOT NULL,
`Headers` text NOT NULL,
`UserID` int(10) unsigned default NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `List` (`List`,`MsgDate`,`MessageId`),
KEY `From` (`From`),
KEY `UserID` (`UserID`,`List`,`MsgDate`),
KEY `MsgDate` (`MsgDate`),
KEY `ListOnly` (`List`)
) TYPE=MyISAM ROW_FORMAT=DYNAMIC
Voici le expliquer:
table type possible_keys key key_len ref rows Extra
------ ------ ------------- -------- ------- ------ ------ --------------------------------------------
m ref List,ListOnly ListOnly 10 const 18002 Using where; Using temporary; Using filesort
Pourquoi est-il en utilisant un fichier de fichiers quand je avoir des index sur toutes les colonnes pertinentes? J'ai ajouté l'index ListOnly juste pour voir si cela aiderait. J'avais initialement pensé que l'index de la liste gérerait à la fois la sélection de la liste et le tri sur MsgDate, mais ce n'était pas le cas. Maintenant que j'ai ajouté l'index ListOnly, c'est celui qu'il utilise, mais il fait toujours un fichier sur MsgDate, ce qui, je le soupçonne, prend tellement de temps.
J'essayé d'utiliser FORCE INDEX comme suit:
SELECT DISTINCT ID,List,`From`,Subject, UNIX_TIMESTAMP(MsgDate) AS FmtDate
FROM messages
FORCE INDEX (List)
WHERE List='general'
ORDER BY MsgDate
LIMIT 17290,20;
Cela ne semble forcer MySQL à utiliser l'index, mais il n'accélère pas la requête du tout.
est ici l'expliquer pour cette requête:
table type possible_keys key key_len ref rows Extra
------ ------ ------------- ------ ------- ------ ------ ----------------------------
m ref List List 10 const 18002 Using where; Using temporary
MISES À JOUR:
J'ai enlevé DISTINCT de la requête. Cela n'a pas aidé la performance du tout.
J'ai supprimé l'appel UNIX_TIMESTAMP. Cela n'a pas non plus affecté les performances.
J'ai fait un cas particulier dans mon code PHP afin que si je perçois l'utilisateur regarde la dernière page de résultats, ajouter une clause WHERE qui ne renvoie que les 7 derniers jours de résultats:
SELECT m.ID,List,From,Subject,MsgDate
FROM messages
WHERE MsgDate>='2009-11-15'
ORDER BY MsgDate DESC
LIMIT 20
C'est beaucoup plus rapide. Cependant, dès que je navigue vers une autre page de résultats, il doit utiliser l'ancien SQL et prend beaucoup de temps à s'exécuter. Je ne peux pas penser à un moyen pratique et réaliste de le faire pour toutes les pages. De plus, faire ce cas particulier rend mon code PHP plus complexe.
Etrangement, la première exécution de la requête d'origine prend beaucoup de temps. Les exécutions ultérieures de la même requête ou d'une requête présentant une page de résultats différente (c'est-à-dire, uniquement les modifications de la clause LIMIT) sont très rapides. La requête ralentit à nouveau si elle n'a pas été exécutée pendant environ 5 minutes.
SOLUTION:
La meilleure solution que je suis venu avec Jason est basé sur Orendorff et l'idée de Juliette. Tout d'abord, je détermine si la page en cours est plus proche du début ou de la fin du nombre total de pages.Si c'est plus proche de la fin, j'utilise ORDER BY MsgDate DESC, applique une limite appropriée, puis inverse l'ordre des enregistrements retournés.
Cela permet de récupérer des pages proches du début ou de la fin du jeu de résultats beaucoup plus rapidement (la première fois prend 4-5 secondes au lieu de 15-30). Si l'utilisateur souhaite naviguer vers une page proche du milieu (actuellement autour de la 430ème page), la vitesse peut redescendre. Mais ce serait un cas rare.
Alors qu'il ne semble pas y avoir de solution parfaite, c'est beaucoup mieux que dans la plupart des cas.
Merci, Jason et Juliette.
Bon point, shylent. Je vais essayer sans DISTINCT. – elmonty
La suppression de DISTINCT n'a pas du tout amélioré les performances. – elmonty
Est-ce que "LIMIT 17290,20" ne correspond pas à la 865ème page de votre requête? Les utilisateurs naviguent-ils vraiment aussi loin dans l'ensemble de données? – Juliet