2012-01-12 3 views
2

J'ai environ 5 millions de lignes dans une table postgres. Je voudrais savoir combien de lignes correspondent start_time >= NOW(), mais malgré un index sur start_time la requête est extrêmement lente (de l'ordre de plusieurs heures).Problèmes de performances des requêtes de date PostgreSQL

EXPLAIN SELECT COUNT(*) FROM core_event WHERE start_time >= NOW(); 
Aggregate (cost=449217.81..449217.82 rows=1 width=0) 
    -> Index Scan using core_event_start_time on core_event (cost=0.00..447750.83 rows=586791 width=0) 
     Index Cond: (start_time >= now()) 

est ici les informations de schéma pour la table:

id   | integer     | not null default nextval('core_event_id_seq'::regclass) 
source  | character varying(100) | not null 
external_id | character varying(100) | 
title  | character varying(250) | not null 
location | geometry     | not null 
start_time | timestamp with time zone | 
stop_time | timestamp with time zone | 
thumb  | character varying(300) | 
image  | character varying(100) | 
image_thumb | character varying(100) | 
address  | character varying(300) | 
description | text      | 
venue_name | character varying(100) | 
website  | character varying(300) | 
city_id  | integer     | 
category | character varying(100) | 
phone  | character varying(50) | 
place_id | integer     | 
image_url | character varying(300) | 
event_type | character varying(200) | 
hidden  | boolean     | not null 
views  | integer     | not null 
added  | timestamp with time zone | 

Je index sur les champs suivants:

city_id 
external_id (unique) 
location 
location_id 
place_id 
start_time 

Est-il un moyen facile pour moi d'accélérer la requête (par exemple, un index partiel), ou vais-je devoir recourir au partitionnement des données par date?

+2

http://wiki.postgresql.org/wiki/Slow_Counting – StefanNch

+0

@StefanNch - Aïe – MatBailie

+0

Cela s'applique-t-il vraiment? En lisant le lien wiki, le lien de @ StefanNch n'est pertinent que lorsque count n'est pas limité à un champ utilisé dans un index. –

Répondre

2

Essayez d'ajouter un partial index comme ce qui suit:

CREATE INDEX core_event_start_time_recent_idx ON core_event (start_time) 
WHERE start_time >= '2011-01-12 0:0'::timestamptz 

Cela va créer un indice relativement faible. La création d'index prendra un certain temps, mais les requêtes comme celle-ci seront beaucoup plus rapidement par la suite.

SELECT count(*) FROM core_event WHERE start_time >= now(); 

L'efficacité de cet indice pour les requêtes contre now() se dégrader lentement au cours du temps, en fonction du nombre de nouvelles lignes arrivent. Mise à jour (= baisse & créer) l'index avec un horodatage plus récent de temps en temps hors heures.
Vous pouvez automatiser cela avec la fonction plpgsql que vous appelez par cronjob ou pgAgent.


Vous pourriez essayer de voir si une course CLUSTER sur la table des choses améliore (si elle ne va pas contre d'autres exigences dans votre db):

CLUSTER core_event USING core_event_start_time; 

Oui, cluster sur l'index complet , pas le partiel. Cela prendra un certain temps et nécessite un verrou exclusif, car il réécrit efficacement la table. Il vide aussi efficacement la table entièrement. Lisez à ce sujet dans le manual. Vous pouvez également augmenter le statistics target pour core_event.start_time;

ALTER core_event ALTER start_time SET STATISTICS 1000; -- example value 

La valeur par défaut est juste 100. Puis:

ANALYZE core_event; 

Ou bien sûr, tous les usual performance stuff applies, too.

+0

J'ai ajouté l'index, mais d'EXPLAIN ANALYZE il ne semble pas l'utiliser. Il fait une analyse Bitmap Index sur core_event_start_time –

+0

@BenDowling: C'est bizarre. J'ai quelques index comme ça et j'ai vérifié que ça marche pour moi (sur les versions 8.4, 9.0 et 9.1). Devrait fonctionner pour n'importe quel horodatage ** après ** celui de la clause where de l'index - pourvu que la plupart des lignes aient un 'start_time' plus ancien que cet horodatage (ou l'index ne sera pas plus rapide qu'un balayage de table séquentiel. cas, il serait choisi sur un index complet sur 'start_time' sur toutes les lignes.Quelque chose ne correspond pas ici.Est-ce que vous montrez l'image complète? Combien de lignes (environ) sont avant et après' now() 'dans votre table ? –

+0

J'utilise 8.4, et j'ai environ 4 millions de lignes au total, et 700.000> = NOW() .J'ai juste lancé ANALYZE core_event, et relance expliquer et il n'utilise toujours pas l'index partiel: –

0

La plupart de ces colonnes sont-elles renseignées pour chaque ligne? Si c'est le cas, la quantité de disque que postgresql doit regarder pour tester la vivacité des lignes même après avoir vérifié l'index sera assez grande. Essayez par exemple la création d'une table séparée qui a seulement id et start_time:

create table core_event_start_time as select id, start_time from core_event; 
alter table core_event_start_time add primary key(id); 
alter table core_event_start_time add foreign key(id) references core_event(id); 
create index on core_event_start_time(start_time); 

Maintenant voir combien de temps il faut compter ID dans core_event_start_time seulement. Bien sûr, cette approche va prendre plus de mémoire cache au détriment de l'espace pour votre table core_event ...Si cela vous aide, vous pouvez ajouter un déclencheur sur core_event pour maintenir la table auxiliaire à jour.

(postgresql 9.2 introduira "index seulement scans" qui peuvent aider à ce genre de situation, mais ce sera pour l'avenir)

Questions connexes