2009-02-26 7 views
4

J'utilise une requête qui s'exécute généralement en moins d'une seconde, mais prend parfois entre 10-40 secondes pour terminer. Je ne suis pas tout à fait clair sur la façon dont la sous-requête fonctionne, je sais juste que cela fonctionne, en ce qu'il me donne 15 lignes pour chaque faverprofileid. Je note les requêtes lentes et 5823244 lignes ont été examinées, ce qui est étrange car il n'y a pas de lignes proches dans les tables concernées (la table des favoris a le plus de 50 000 lignes) .Astuces pour améliorer cette requête mysql lente?

Quelqu'un peut-il me proposer des pointeurs? Est-ce un problème avec la sous-requête et l'utilisation de filesort?

EDIT: L'exécution expliquera que la table users n'utilise pas d'index (même si id est la clé primaire). Sous extra, il est dit: Utilisation temporaire; Utilisation de fichiers

SELECT F.id,F.created,U.username,U.fullname,U.id,I.* 
FROM favorites AS F 
INNER JOIN users AS U ON F.faver_profile_id = U.id 
INNER JOIN items AS I ON F.notice_id = I.id 
WHERE faver_profile_id IN (360,379,95,315,278,1) 
AND F.removed = 0 
AND I.removed = 0 
AND F.collection_id is null 
AND I.nudity = 0 
AND (SELECT COUNT(*) FROM favorites WHERE faver_profile_id = F.faver_profile_id 
AND created > F.created AND removed = 0 AND collection_id is null) < 15 
ORDER BY F.faver_profile_id, F.created DESC; 
+0

Vous pourriez obtenir une meilleure coloration syntaxique si vous écrivez les mots-clés "select", "from", "where", "and", "in" et "order by" en lettres minuscules, et "inner", "join" , "on" et "desc" avec seulement une première lettre majuscule. –

+0

Etes-vous en train d'essayer de sélectionner les 15 premiers éléments pour chaque faver_profile_id ordonné par créé? – Quassnoi

+0

Avez-vous exécuté ANALYZE sur les tables? – vladr

Répondre

5

Je pense avec GROUP BY et HAVING il devrait être plus rapide. Est-ce ce que vous voulez?

SELECT F.id,F.created,U.username,U.fullname,U.id, I.field1, I.field2, count(*) as CNT 
FROM favorites AS F 
INNER JOIN users AS U ON F.faver_profile_id = U.id 
INNER JOIN items AS I ON F.notice_id = I.id 
WHERE faver_profile_id IN (360,379,95,315,278,1) 
AND F.removed = 0 
AND I.removed = 0 
AND F.collection_id is null 
AND I.nudity = 0 
GROUP BY F.id,F.created,U.username,U.fullname,U.id,I.field1, I.field2 
HAVING CNT < 15 
ORDER BY F.faver_profile_id, F.created DESC; 

Je ne sais pas quels sont les champs de items dont vous avez besoin, donc j'ai mis des espaces réservés.

+0

en utilisant group by et having est ce qui est apparu dans ma tête en premier quand j'ai lu son problème ... – GordonB

4

Le nombre de lignes examinées représente est important car de nombreuses lignes ont été examinées plusieurs fois. Vous obtenez cela en raison d'un plan de requête incorrectement optimisé qui entraîne des analyses de table lorsque les recherches d'index doivent avoir été effectuées. Dans ce cas, le nombre de rangées examinées est exponentiel, c'est-à-dire d'un ordre de grandeur comparable au produit du nombre total de rangées dans plusieurs tables.

  • Assurez-vous que vous avez course ANALYZE TABLE sur vos trois tables.
  • Lire sur how to avoid table scans et identifier ensuite créer des index manquants
  • Rerun ANALYSER et réexpliquer vos requêtes
    • le nombre de lignes examinées doivent baisser de façon spectaculaire
    • sinon, publier pleinement expliquer le plan
  • utilisation query hints pour forcer l'utilisation des indices (voir les noms d'index pour une table, utilisez SHOW INDEX):

SELECT F.id,F.created,U.username,U.fullname,U.id,I.*
FROM favorites AS FFORCE INDEX (faver_profile_id_key)
INNER JOIN users AS UFORCE INDEX FOR JOIN (PRIMARY)ON F.faver_profile_id = U.id
INNER JOIN items AS IFORCE INDEX FOR JOIN (PRIMARY)ON F.notice_id = I.id
WHERE faver_profile_id IN (360,379,95,315,278,1)
AND F.removed = 0
AND I.removed = 0
AND F.collection_id is null
AND I.nudity = 0
AND (SELECT COUNT(*) FROM favoritesFORCE INDEX (faver_profile_id_key)WHERE faver_profile_id = F.faver_profile_id
AND created > F.created AND removed = 0 AND collection_id is null) < 15
ORDER BY F.faver_profile_id, F.created DESC;

Vous pouvez également modifier votre requête à utiliser GROUP BY faver_profile_id/HAVING count > 15 au lieu de la sous-requête SELECT COUNT(*) imbriqué, comme suggéré par vartec . Les performances de votre requête d'origine et de vartec doivent être comparables si les deux sont correctement optimisées, par ex. à l'aide des notes (votre requête utiliserait imbriquées recherches d'index, alors que la requête de vartec utiliserait une stratégie basée sur le hachage.)

3

Je vous suggère d'utiliser Mysql Explain Query pour voir comment votre serveur mysql gère la requête. Mon pari est que vos index ne sont pas optimaux, mais expliquer devrait faire beaucoup mieux que mon pari.

+0

Oui, en cours d'exécution montre que la table des utilisateurs n'utilise pas d'index (même si id est la clé primaire). Sous extra, il est dit: Utilisation temporaire; Utilisation de fichiers Je ne sais pas pourquoi il n'utiliserait pas l'index .. – makeee

0

Vous pouvez faire une boucle sur chaque identifiant et limite l'utilisation au lieu du nombre (*) sous-requête:

foreach $id in [123,456,789]: 
    SELECT 
    F.id, 
    F.created, 
    U.username, 
    U.fullname, 
    U.id, 
    I.* 
    FROM 
    favorites AS F INNER JOIN 
    users AS U ON F.faver_profile_id = U.id INNER JOIN 
    items AS I ON F.notice_id = I.id 
    WHERE 
    F.faver_profile_id = {$id} AND 
    I.removed = 0 AND 
    I.nudity = 0 AND 
    F.removed = 0 AND 
    F.collection_id is null 
    ORDER BY 
    F.faver_profile_id, 
    F.created DESC 
    LIMIT 
    15; 
0

Je vais supposer que le résultat de cette requête est intented à afficher une liste paginée. Dans ce cas, vous pourriez peut-être envisager de faire une «requête non jointe» plus simple et faire une deuxième requête pour chaque ligne afin de ne lire que les 15, 20 ou 30 éléments affichés. N'était-ce pas un JOIN une opération lourde? Cela simplifierait la requête et ne deviendrait pas plus lent lorsque les tables jointes se développeraient.

Dites-moi si je me trompe, s'il vous plaît.

Questions connexes