J'utilise Postgres 9.3 via Heroku.
J'ai une table, "trafic", avec 1M + d'enregistrements qui a de nombreux insertions et mises à jour chaque jour. J'ai besoin d'effectuer des opérations SUM sur cette table sur différentes plages de temps et ces appels peuvent prendre jusqu'à 40 secondes et j'aimerais entendre des suggestions sur la façon d'améliorer cela.
J'ai l'index suivant en place sur cette table:
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Voici un exemple d'instruction SELECT:
SELECT SUM("clicks") AS clicks, SUM("impressions") AS impressions
FROM "traffic"
WHERE "uuid_self" != "uuid_partner"
AND "campaign_id" is NULL
AND "dt_created" >= 'Sun, 29 Mar 2015 00:00:00 +0000'
AND "dt_created" <= 'Mon, 27 Apr 2015 23:59:59 +0000'
Et voici l'ANALYSE EXPLICITE:
Aggregate (cost=21625.91..21625.92 rows=1 width=16) (actual time=41804.754..41804.754 rows=1 loops=1)
-> Index Scan using idx_traffic_partner_only on traffic (cost=0.09..20085.11 rows=308159 width=16) (actual time=1.409..41617.976 rows=302392 loops=1)
Index Cond: ((dt_created >= '2015-03-29'::date) AND (dt_created <= '2015-04-27'::date))
Total runtime: 41804.893 ms
http://explain.depesz.com/s/gGA
Cette question est très similaire à une autre sur SE, mais celle-ci a utilisé un index sur deux plages d'horodatage de colonne et le planificateur d'index pour cette requête avait des estimations très éloignées. La principale suggestion était de créer un index multi-colonnes trié, mais pour les index à colonne unique, cela n'a pas beaucoup d'effet. Les autres suggestions étaient d'utiliser les index CLUSTER / pg_repack et GIST, mais je ne les ai pas encore essayés, car j'aimerais voir s'il existe une meilleure solution en utilisant des index réguliers.
Optimisation des requêtes sur une plage d'horodatages (deux colonnes)
Pour référence, j'ai essayé les index suivants, qui n'étaient pas utilisés par la base de données:
INDEX idx_traffic_2 ON traffic (campaign_id, uuid_self, uuid_partner, dt_created);
INDEX idx_traffic_3 ON traffic (dt_created);
INDEX idx_traffic_4 ON traffic (uuid_self);
INDEX idx_traffic_5 ON traffic (uuid_partner);
EDIT : Ran EXPLAIN (ANALYSER, VERBOSE, COÛTS, TAMPONS) et voici les résultats:
Aggregate (cost=20538.62..20538.62 rows=1 width=8) (actual time=526.778..526.778 rows=1 loops=1)
Output: sum(clicks), sum(impressions)
Buffers: shared hit=47783 read=29803 dirtied=4
I/O Timings: read=184.936
-> Index Scan using idx_traffic_partner_only on public.traffic (cost=0.09..20224.74 rows=313881 width=8) (actual time=0.049..431.501 rows=302405 loops=1)
Output: id, uuid_self, uuid_partner, impressions, clicks, dt_created... (other fields redacted)
Index Cond: ((traffic.dt_created >= '2015-03-29'::date) AND (traffic.dt_created <= '2015-04-27'::date))
Buffers: shared hit=47783 read=29803 dirtied=4
I/O Timings: read=184.936
Total runtime: 526.881 ms
http://explain.depesz.com/s/7Gu6
Définition du tableau:
CREATE TABLE traffic (
id serial,
uuid_self uuid not null,
uuid_partner uuid not null,
impressions integer NOT NULL DEFAULT 1,
clicks integer NOT NULL DEFAULT 0,
campaign_id integer,
dt_created DATE DEFAULT CURRENT_DATE NOT NULL,
dt_updated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
)
id est la clé primaire et uuid_self, uuid_partner et campaign_id sont toutes des clés étrangères. Le champ dt_updated est mis à jour avec une fonction postgres.
la source
explain (buffers, analyze, verbose) ...
pourrait apporter plus de lumière.traffic
. Aussi: pourquoi la secondeEXPLAIN
montre-t-elle une baisse de 42 sec à 0,5 sec? La première exécution a-t-elle été effectuée avec un cache froid?id
? D'autres contraintes? Je vois deux colonnes qui peuvent être NULL. Quel est le pourcentage de valeurs NULL dans chacune? Qu'obtenez-vous pour cela?SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
Réponses:
Deux choses très étranges ici:
La requête sélectionne 300k lignes dans une table avec 1M + lignes. Pour 30% (ou plus de 5% - cela dépend de la taille de la ligne et d'autres facteurs), il n'est généralement pas avantageux d'utiliser un index. Nous devrions voir un balayage séquentiel .
L'exception serait les analyses d'index uniquement, que je ne vois pas ici. L'index à plusieurs colonnes @Craig suggéré serait la meilleure option si vous obtenez des analyses d'index uniquement. Avec de nombreuses mises à jour comme vous l'avez mentionné, cela peut ne pas fonctionner, auquel cas vous feriez mieux sans les colonnes supplémentaires - et juste l'index que vous avez déjà. Vous pourrez peut-être le faire fonctionner pour vous avec des paramètres de vide automatique plus agressifs pour la table. Vous pouvez ajuster les paramètres de chaque table.
Alors que Postgres va utiliser l'index, je m'attendrais certainement à voir un scan d'index bitmap pour autant de lignes, pas un scan d'index simple, qui est généralement le meilleur choix pour un faible pourcentage de lignes. Dès que Postgres attend plusieurs hits par page de données (à en juger par ses statistiques sur la table), il passe généralement à un scan d'index bitmap.
À en juger, je soupçonne que vos paramètres de coût sont insuffisants (et peut-être les statistiques du tableau aussi). Vous avez peut-être réglé
random_page_cost
et / ou trop bas par rapport à . Suivez les liens et lisez le manuel.cpu_index_tuple_cost
seq_page_cost
Conviendrait également à l'observation selon laquelle le cache froid est un facteur important, comme nous l'avons expliqué dans les commentaires. Soit vous accédez à (des parties de) tables que personne n'a touchées depuis longtemps ou vous utilisez un système de test où le cache n'est pas (encore) rempli?
Sinon, vous n'avez tout simplement pas assez de RAM disponible pour mettre en cache la plupart des données pertinentes dans votre base de données. Par conséquent, l'accès aléatoire est beaucoup plus cher que l'accès séquentiel lorsque les données résident dans le cache. Selon la situation réelle, vous devrez peut-être vous ajuster pour obtenir de meilleurs plans de requête.
Un autre facteur doit être mentionné pour la réponse lente lors de la première lecture seule: les bits de conseil . Lisez les détails dans le wiki Postgres et cette question connexe:
Ou le tableau est extrêmement gonflé , auquel cas un balayage d'index aurait du sens et je me référerais à
CLUSTER
/pg_repack
dans ma réponse précédente que vous avez citée. (Ou tout simplementVACUUM FULL)
Et examinez vosVACUUM
paramètres. Ceux-ci sont importants avecmany inserts and updates every day
.En fonction des
UPDATE
modèles, considérez également une valeurFILLFACTOR
inférieure à 100. Si vous mettez à jour uniquement les lignes nouvellement ajoutées, définissez la valeur inférieureFILLFACTER
après avoir compacté votre tableau, de sorte que seules les nouvelles pages conservent une certaine marge de manœuvre pour les mises à jour.Schéma
Ajustez légèrement la séquence des colonnes pour économiser 8 octets par ligne (dans 99% des cas où
campaign_id
est NULL):Explication détaillée et liens vers plus:
Mesurer:
la source
Il me semble que vous interrogez beaucoup de données dans un gros index, donc c'est lent. Rien de mal là-bas.
Si vous utilisez PostgreSQL 9.3 ou 9.4, vous pouvez essayer de voir si vous pouvez obtenir une analyse d'index uniquement en faisant un index couvrant de toutes sortes.
PostgreSQL n'a pas de véritables index de couverture ou de prise en charge de termes d'index qui ne sont que des valeurs, ne faisant pas partie de l'arborescence b, c'est donc plus lent et plus cher que cela ne pourrait l'être avec ces fonctionnalités. Cela pourrait tout de même être une victoire sur un simple balayage d'index si le vide fonctionne suffisamment souvent pour maintenir la carte de visibilité à jour.
Idéalement, PostgreSQL prend en charge les champs de données auxiliaires dans un index comme dans MS-SQL Server ( cette syntaxe NE FONCTIONNERA PAS dans PostgreSQL ):
la source
random_page_cost
etc.). De plus, à des fins de test, ne voyez que siset enable_indexscan = off
,set enable_seqscan = off
puis la réexécution force une analyse d'index uniquement, et si oui, quelles sont ses estimations de coûts à partir d'analyser.