J'ai une table PostgreSQL 9.3 avec quelques chiffres et quelques données supplémentaires:
CREATE TABLE mytable (
myid BIGINT,
somedata BYTEA
)
Cette table compte actuellement environ 10 millions d'enregistrements et occupe 1 Go d'espace disque. myid
ne sont pas consécutifs.
Je veux calculer le nombre de lignes dans chaque bloc de 100 000 nombres consécutifs:
SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
Cela renvoie environ 3500 lignes.
J'ai remarqué que l'existence d'un certain index accélère considérablement cette requête même si le plan de requête ne le mentionne pas du tout. Le plan de requête sans l'index:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=1636639.92..1709958.65 rows=496942 width=8) (actual time=6783.763..8888.841 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Sort (cost=1636639.92..1659008.91 rows=8947594 width=8) (actual time=6783.752..8005.831 rows=8947557 loops=1)
Output: ((myid / 100000))
Sort Key: ((mytable.myid / 100000))
Sort Method: external merge Disk: 157440kB
-> Seq Scan on public.mytable (cost=0.00..236506.92 rows=8947594 width=8) (actual time=0.020..1674.838 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 8914.780 ms
(9 rows)
L'index:
db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;
Le nouveau plan de requête:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=281242.99..281285.97 rows=3439 width=8) (actual time=3190.189..3190.800 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Seq Scan on public.mytable (cost=0.00..236505.56 rows=8947485 width=8) (actual time=0.026..1659.571 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 3190.975 ms
(5 rows)
Ainsi, les plans de requête et les temps d'exécution diffèrent considérablement (près de trois fois) mais ne mentionnent pas l'index. Ce comportement est parfaitement reproductible sur ma machine de développement: j'ai effectué plusieurs cycles de suppression de l'index, de test de la requête plusieurs fois, de recréation de l'index, de nouveau de tester la requête plusieurs fois. Qu'est-ce qu'il se passe ici?
HashAggregate
méthode (et aucun tri n'est requis), donc vous obtenez de meilleures performances. Pourquoi l'indice n'est pas mentionné dans le plan, je n'en ai aucune idée.explain (analyze true, verbose true) ...
:?Réponses:
VACUUM ANALYZE
fait la différence dans votre exemple. De plus, comme @jjanes l'a fourni , les statistiques supplémentaires pour l'index fonctionnel. Par documentation:Cependant, la création de l'index ne provoque pas à elle seule la collecte de statistiques par Postgres. Essayer:
Ne renvoie rien tant que vous n'avez pas exécuté votre premier
ANALYZE
(ouVACUUM ANALYZE
, ou que le démon autovacuum entre en action).Vous verrez maintenant des statistiques supplémentaires.
Étant donné que la table entière doit être lue de toute façon, Postgres va utiliser un scan séquentiel à moins qu'il ne s'attende à ce que le calcul
myid/100000
soit assez coûteux pour basculer, ce qui n'est pas le cas.Votre seule autre chance serait une analyse d'index uniquement si l'index est beaucoup plus petit que la table - et les conditions préalables à une analyse d'index uniquement sont remplies. Détails dans le wiki Postgres et dans le manuel .
Tant que cet indice fonctionnel n'est pas utilisé, l'avantage collatéral des statistiques ajoutées est modéré. Si la table était en lecture seule, le coût serait faible - mais là encore, nous verrions probablement une analyse indexée immédiatement.
Vous pouvez peut-être également obtenir de meilleurs plans de requête en définissant un objectif de statistiques plus élevé pour
mytable.myid
. Cela n'entraînerait qu'un coût mineur. Plus:la source
myid/100000 BETWEEN somevalue AND othervalue
condition supplémentaire , donc l'index sera utilisé dans le plan de requête de toute façon - je viens de poser cette question parce que je ne comprenais pas pourquoi l'index est utile dans le cas de la table entière.WHERE myid BETWEEN somevalue*100000 AND othervalue*100000
(pensez aux effets d'arrondi en fonction de vos types), et vous avez probablement déjà un index simplemyid
, vous pouvez donc vous passer d'un index spécialisé supplémentaire. Pourrait être plus efficace.Lorsque vous créez un index d'expression, PostgreSQL collecte des statistiques sur cette expression. Grâce à ces statistiques, il dispose désormais d'une estimation précise du nombre de lignes agrégées que la requête renverra, ce qui l'amène à faire un meilleur choix de plan.
Plus précisément dans ce cas, sans ces statistiques supplémentaires, il pensait que la table de hachage serait trop grande pour tenir dans work_mem, donc il n'a pas choisi cette méthode.
la source
work_mem
. Si vous l'avez élevé de sorte que le tri tienne dans la mémoire, il utiliserait toujours le même plan. Permettez-moi de noter ici que la différence de temps (la plupart du temps) provient du type de disque externe.