2009-07-27 6 views
1

Il s'agit d'une requête qui totalise les résultats de jeu de chaque joueur et affiche les joueurs qui correspondent aux conditions.Quelle est la meilleure façon d'optimiser cette requête MySQL?

select *, 
     (kills/deaths) as killdeathratio, 
     (totgames - wins) as losses 
from  (select gp.name    as name, 
        gp.gameid   as gameid, 
        gp.colour   as colour, 
        Avg(dp.courierkills) as courierkills, 
        Avg(dp.raxkills)  as raxkills, 
        Avg(dp.towerkills) as towerkills, 
        Avg(dp.assists)  as assists, 
        Avg(dp.creepdenies) as creepdenies, 
        Avg(dp.creepkills) as creepkills, 
        Avg(dp.neutralkills) as neutralkills, 
        Avg(dp.deaths)  as deaths, 
        Avg(dp.kills)  as kills, 
        sc.score    as totalscore, 
        Count(*)   as totgames, 
        Sum(case 
         when ((dg.winner = 1 and dp.newcolour < 6) or 
           (dg.winner = 2 and dp.newcolour > 6)) 
         then 1 
         else 0 
         end) as wins 
      from  gameplayers as gp, 
        dotagames as dg, 
        games  as ga, 
        dotaplayers as dp, 
        scores  as sc 
      where dg.winner <> 0 
        and dp.gameid = gp.gameid 
        and dg.gameid = dp.gameid 
        and dp.gameid = ga.id 
        and gp.gameid = dg.gameid 
        and gp.colour = dp.colour 
        and sc.name = gp.name 
      group by gp.name 
      having totgames >= 30 
     ) as h 
order by totalscore desc 

Maintenant, je ne suis pas trop sûr quelle est la meilleure façon d'aller, mais ce serait à votre avis est d'optimiser cette requête? Je lance un Q6600 @ 2.4ghz, 4gb de RAM, un système Linux Ubuntu 9.04 64 bits et cette requête peut prendre jusqu'à 6.7 secondes (j'ai une énorme base de données).

Aussi je voudrais paginer les résultats aussi bien et l'exécution des conditions supplémentaires au-dessus de cette requête est beaucoup trop lent ....

J'utilise django comme frontend donc toutes les méthodes qui incluent l'utilisation de Python +/- Les méthodes django seraient géniales. MySQL, Apache2 tweaks sont également les bienvenus. Et bien sûr, je suis ouvert à changer la requête pour la faire fonctionner plus vite.

Merci d'avoir lu ma question; hâte de lire vos réponses!

Edit: EXPLIQUER Regrouper les résultats

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 PRIMARY  <derived2> ALL  NULL NULL NULL NULL 783  Using filesort 
2 DERIVED  sc ALL  name,name_2  NULL NULL NULL 2099 Using temporary; Using filesort 
2 DERIVED  gp ref  gameid,colour,name name 17 development.sc.name  2  
2 DERIVED  ga eq_ref PRIMARY,id,id_2  PRIMARY  4 development.gp.gameid 1 Using index 
2 DERIVED  dg ref  gameid,winner gameid 4 development.ga.id 1 Using where 
2 DERIVED  dp ref  gameid_2,colour  gameid_2 4 development.ga.id 10 Using where 
+1

Que dit EXPLAIN de la requête? Cela donne-t-il des indications sur les index potentiels que vous pourriez créer? –

+0

Mis à jour avec les résultats d'explication; va ajouter ces clés et voir si les résultats ont changé. – fivetwentysix

+0

En outre, puisque vous sélectionnez à partir d'une table dérivée, vous pouvez essayer de "déplacer" la table dérivée dans une vue. Cela peut aider à accélérer un peu la requête puisque "hints" devrait être disponible pour la vue (c'est la connaissance prise de MS SqlServer, donc je ne suis pas sûr combien cela s'applique à MySql). –

Répondre

2

Tout d'abord, le SQL est mal formaté. L'erreur la plus évidente est la division de ligne avant chaque AS clause. Deuxième problème évident est l'utilisation de jointures implicites au lieu d'utiliser explicitement INNER JOIN ... ON ....

Maintenant, pour répondre à la question. Sans connaître les données ou l'environnement, la première chose que je regarderais serait quelques-uns des paramètres du serveur MySQL, tels que sort_buffer et key_buffer. Si vous n'avez rien changé, allez lire sur eux. Les valeurs par défaut sont extrêmement conservatrices et peuvent souvent être augmentées de plus de dix fois leur défaut, en particulier sur le gros fer comme vous l'avez fait. Après avoir examiné cela, je serais en cours d'exécution des morceaux de la requête pour voir la vitesse et ce que dit EXPLAIN. L'effet de l'indexation peut être profond, mais MySQL a un problème avec les doigts et les orteils où il ne peut pas en utiliser plus d'un par table. Et JOIN s avec le filtrage peut avoir besoin de deux. Donc, il doit descendre à un rowscan pour l'autre vérification. Mais cela étant dit, découper la requête et essayer différentes combinaisons vous montrera où elle commence à trébucher. Maintenant, vous aurez une idée où un "point de bascule" pourrait être: c'est là qu'une petite augmentation de la taille des données brutes, comme combien il doit extraire, entraînera une grande perte de la structure devient trop grande. À ce stade, vous souhaiterez probablement augmenter la taille des tables temporaires. Attention, ce type d'optimisation est un peu un art noir. :-)

Cependant, il existe une autre approche: la dénormalisation. Dans une implémentation simple, les scripts régulièrement programmés exécuteront cette requête coûteuse de temps en temps et placeront les données dans une table distincte dans une structure beaucoup plus proche de ce que vous voulez afficher. Il existe plusieurs variantes de cette approche. Il peut être possible de le garder à jour à la volée, soit dans l'application, soit en utilisant des déclencheurs de table. À l'autre extrême, vous pouvez autoriser votre application à exécuter occasionnellement la requête coûteuse, mais mettre en cache le résultat pendant un petit moment. Ceci est plus efficace si beaucoup de gens l'appellent souvent: même un cache de 2 secondes sur une requête lancée 15 fois par seconde montrera une amélioration visible.

Vous pouvez trouver des moyens de produire les mêmes données en exécutant une demi-douzaine de requêtes qui renvoient chacune des données et post-traitent les données. Vous pouvez également exécuter la version de votre requête d'origine qui renvoie plus de données (ce qui est susceptible d'être beaucoup plus rapide car elle fait moins de filtrage) et post-traiter cela. J'ai trouvé plusieurs fois que cinq requêtes plus simples et plus petites peuvent être beaucoup plus rapides - un ordre de grandeur, parfois deux - qu'une grande requête qui essaie de tout faire.

0

Aucun index ne vous aidera puisque vous analysez des tables entières. Au fur et à mesure que votre base de données grandit, la requête devient toujours plus lente. Pensez à accumuler les statistiques: après chaque partie, insérez la ligne pour ce jeu, et incrémentez aussi les marqueurs dans la rangée du joueur, alors vous n'avez pas besoin de count() et sum() parce que l'information est disponible.

0
  • select * est mauvais la plupart du temps - sélectionnez uniquement les colonnes dont vous avez besoin
  • pause la sélection en plusieurs sélections simples, utiliser des tables temporaires en cas de besoin
  • la somme (partie de cas pourrait être fait avec un subselect
  • MySQL a une très mauvaise performance avec ou-expressions. utiliser deux sélections qui vous l'union ensemble
0

petite amélioration

select *, (kills/deaths) as killdeathratio, (totgames - wins) as losses from (select gp.name as name, gp.gameid as gameid, gp.colour as colour, Avg(dp.courierkills) as courierkills, Avg(dp.raxkills) as raxkills, Avg(dp.towerkills) as towerkills, Avg(dp.assists) as assists, Avg(dp.creepdenies) as creepdenies, Avg(dp.creepkills) as creepkills, Avg(dp.neutralkills) as neutralkills, Avg(dp.deaths) as deaths, Avg(dp.kills) as kills, sc.score as totalscore, Count(1) as totgames, Sum(case when ((dg.winner = 1 and dp.newcolour < 6) or (dg.winner = 2 and dp.newcolour > 6)) then 1 else 0 end) as wins from gameplayers as gp, ( select * from dotagames dg1 where dg.winner <> 0) as dg, games as ga, dotaplayers as dp, scores as sc where and dp.gameid = gp.gameid and dg.gameid = dp.gameid and dp.gameid = ga.id and gp.gameid = dg.gameid and gp.colour = dp.colour and sc.name = gp.name group by gp.name having totgames >= 30 ) as h order by totalscore desc

Changements: 1. COUNT (*) chnaged compter (1) 2. Dans le FROM, le nombre de lignes sont réduites.

+0

Pourriez-vous éditer votre question, et ajouter quelques explications sur ce que vous avez changé, et comment cela va améliorer la requête? – andrewsi

Questions connexes