2011-01-31 2 views
0

J'ai une application Rails (fonctionnant sur un compte Heroku) qui saisit un tas de statistiques pour la page d'accueil concernant le nombre d'enregistrements qui correspondent à certains critères. Chaque compte est affiché sous la forme d'un nombre sur la page. Ma table (listings) se compose d'environ 22 500 enregistrements. En production, il faut environ 350ms pour charger la page (toujours en dessous du seuil mais pas génial pour une page d'accueil).Est-il possible d'optimiser ces requêtes SQL plus loin?

Veuillez prendre en compte le nombre de requêtes ici, je voulais décrire la redondance de ce que j'essaie de faire. Ce sent comme cela pourrait être fait beaucoup plus efficacement. Des idées?

SELECT COUNT(1) FROM listings WHERE (city in ('Syracuse')) 
SELECT COUNT(1) FROM listings WHERE (city in ('Syracuse')) AND (created_at >= '2011-01-30 18:28:44.656702') 
SELECT COUNT(1) FROM listings WHERE (city in ('Cicero', 'Clay', 'Lysander', 'VanBuren', 'Salina')) 
SELECT COUNT(1) FROM listings WHERE (city in ('Cicero', 'Clay', 'Lysander', 'VanBuren', 'Salina')) AND (created_at >= '2011-01-30 18:28:44.811090') 
SELECT COUNT(1) FROM listings WHERE (city in ('DeWitt', 'Manlius', 'Pompey')) 
SELECT COUNT(1) FROM listings WHERE (city in ('DeWitt', 'Manlius', 'Pompey')) AND (created_at >= '2011-01-30 18:28:44.954442') 
SELECT COUNT(1) FROM listings WHERE (city in ('Onondaga', 'Elbridge', 'Geddes', 'Camillus')) 
SELECT COUNT(1) FROM listings WHERE (city in ('Onondaga', 'Elbridge', 'Geddes', 'Camillus')) AND (created_at >= '2011-01-30 18:28:45.105438') 
SELECT COUNT(1) FROM listings WHERE (city in ('Fabius', 'Lafayette', 'Marcellus', 'Otisco', 'Skaneateles', 'Spafford', 'Tully')) 
SELECT COUNT(1) FROM listings WHERE (city in ('Fabius', 'Lafayette', 'Marcellus', 'Otisco', 'Skaneateles', 'Spafford', 'Tully')) AND (created_at >= '2011-01-30 18:28:45.258860') 
SELECT COUNT(1) FROM listings WHERE (city in ('West Monroe', 'Hastings', 'Constantia', 'Palermo', 'Mexico', 'Parish', 'Schroeppel')) 
SELECT COUNT(1) FROM listings WHERE (city in ('West Monroe', 'Hastings', 'Constantia', 'Palermo', 'Mexico', 'Parish', 'Schroeppel')) AND (created_at >= '2011-01-30 18:28:45.411138') 

Une option que je considérais utilise les after_add et after_remove crochets sur mon modèle de cotation mettre à jour une table séparée avec ces statistiques. Ma seule préoccupation à ce sujet est les problèmes de maintenance impliqués. Cependant, les nouvelles listes ne sont ajoutées que quelques fois au cours de la journée, donc la mise à jour de cette table ne devrait pas causer de problèmes de performance en soi.

Merci!

+0

Quel type de base de données est-il? – FrustratedWithFormsDesigner

+0

Il utilise par défaut la base de données Postgres Heroku. J'utilise aussi le compte gratuit Heroku (peut-être le coupable?). – Mike

Répondre

4

Différentes approches, pas toutes orientées base de données.

Vous pouvez combiner toutes les sélections en une seule requête comme ceci:

SELECT COUNT(CASE WHEN city = 'Syracuse' THEN 1 END) as syracuse, 
     COUNT(CASE WHEN city = 'Syracuse' AND created_at >= '2011-01-30 18:28:44.656702' THEN 1 END) as syracuse_recent, 
     /* etc... */ 
FROM listings 

Ce sera juste un balayage sur la table pour recueillir toutes les statistiques.

Alternativement/en plus, mettez en mémoire cache les statistiques extraites de la base de données en mémoire dans votre application, ou utilisez quelque chose comme memcached. S'il n'est pas nécessaire que les statistiques soient exactes à la minute, cela décharge complètement la requête de la base de données, après la population initiale.

+1

Pas besoin de mettre ma réponse dedans, mais j'allais suggérer deux requêtes, une avec les conditions filtrées par date et une sans. Puis éventuellement deux index accordés aux deux requêtes. –

+0

Si toutes les requêtes vont couvrir tout le tableau finalement (ce qui semble probable), l'utilisation d'un index est peu susceptible d'être un avantage. Surtout dans PostgreSQL qui doit référencer les données de tas de toute façon. – araqnid

+0

Merci pour votre réponse. Est-ce que cela commence à devenir un peu trop gnostique avec l'instruction CASE? Les instructions CASE fonctionneront-elles dans toutes les bases de données? – Mike

0

Vous devez d'abord examiner les index que vous avez sur les tables (essayez d'ajouter et de supprimer des index sur des champs individuels ainsi que des index composites dans les deux sens).

Assurez-vous également d'analyser exactement ce que composent les 350ms (avec firebug ou quelque chose comme YSlow).

Enfin, si vous avez vraiment des mises à jour rares et que vous voulez maintenir un tableau récapitulatif, les hooks ne sont pas le seul moyen - vous pouvez également écrire le trigger qui fera ce travail pour vous.

0

Personnellement, j'ajouterais deux nouvelles tables, une qui contient des groupes de villes et l'autre une table de liens de plusieurs à plusieurs entre les groupes et les villes. Vous auriez besoin de "city_group_id", "city_group_name", "dt_count_threshold". La deuxième table serait "city_group_id", "city_id". Ensuite, vous pouvez effectuer des sélections sur la table de liens many to many et rejoindre la table city avec votre restriction de date/heure. Gardez à l'esprit que ces requêtes ne sont pas testées et que des ajustements mineurs sont nécessaires. Et assurez-vous que tous les index sont correctement configurés pour éviter les analyses de table.

Questions connexes