Vous n'êtes pas identifié(e).
Pages : 1
Bonjour,
Il y a quelque chose qui me laisse perplexe, c'est la non utilisation de l'index BRIN sur un max() :
aegir=# select version();
version
--------------------------------------------------------------------------------------------------------------------------------------
PostgreSQL 14.9 (Ubuntu 14.9-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
(1 row)
aegir=# create table example (id bigint);
CREATE TABLE
aegir=# create index brin_example on example using brin(id);
CREATE INDEX
aegir=# insert into example ( select generate_series(1,1000000));
INSERT 0 1000000
aegir=# select pg_size_pretty(pg_relation_size('example'));
pg_size_pretty
----------------
35 MB
(1 row)
aegir=# select pg_size_pretty(pg_relation_size('brin_example'));
pg_size_pretty
----------------
24 kB
(1 row)
aegir=# explain (analyze, verbose) select max(id) from example;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=10633.55..10633.56 rows=1 width=8) (actual time=80.798..90.351 rows=1 loops=1)
Output: max(id)
-> Gather (cost=10633.33..10633.54 rows=2 width=8) (actual time=80.732..90.344 rows=3 loops=1)
Output: (PARTIAL max(id))
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=9633.33..9633.34 rows=1 width=8) (actual time=55.675..55.676 rows=1 loops=3)
Output: PARTIAL max(id)
Worker 0: actual time=43.249..43.250 rows=1 loops=1
Worker 1: actual time=43.237..43.238 rows=1 loops=1
-> Parallel Seq Scan on public.example (cost=0.00..8591.67 rows=416667 width=8) (actual time=0.009..26.193 rows=333333 loops=3)
Output: id
Worker 0: actual time=0.011..20.722 rows=251488 loops=1
Worker 1: actual time=0.011..20.815 rows=251764 loops=1
Planning Time: 0.192 ms
Execution Time: 90.382 ms
(16 rows)
Pourquoi diable le BRIN n'est pas utilisé ? Je pensais qu'il suffisait de faire le max de toutes les bornes supérieures pour avoir le max() non ?
Évidemment, avec un index btree, l'index est bien utilisé :
aegir=# create index bt_example on example(id);
CREATE INDEX
aegir=# explain (analyze, verbose) select max(id) from example;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Result (cost=0.45..0.46 rows=1 width=8) (actual time=0.026..0.027 rows=1 loops=1)
Output: $0
InitPlan 1 (returns $0)
-> Limit (cost=0.42..0.45 rows=1 width=8) (actual time=0.024..0.025 rows=1 loops=1)
Output: example.id
-> Index Only Scan Backward using bt_example on public.example (cost=0.42..28480.42 rows=1000000 width=8) (actual time=0.023..0.024 rows=1 loops=1)
Output: example.id
Index Cond: (example.id IS NOT NULL)
Heap Fetches: 0
Planning Time: 0.165 ms
Execution Time: 0.039 ms
(11 rows)
Il y a quelque chose qui m'échappe au sujet des BRIN ?
Hors ligne
D'après la discussion https://www.postgresql.org/message-id/1 … .pgh.pa.us, il s'agit d'une limite connue des index BRIIN.
Peut-être qu'il s'agit plus d'une limite du "query planner" ?
Pierre
Hors ligne
Il s'agit plutôt d'une limitation générale dans l'infrastructure de postgres. Pour l'instant, les seuls moyens de récupérer un min() ou max() sont:
- renvoyer les donneés triées dans l'ordre voulu et s'arrêter une fois une valeur trouvée
- lire toutes les données et conserver la valeur maximum trouvée
La première approche n'est intéressante que si on peut retourner les données triées dans l'ordre voulu, ce qui n'est le cas que pour un index btree.
Utiliser un index brin pour récupérer la valeur max sans lire toutes les données de la table serait théoriquement possible, mais nécessiterait de développer un nouveau mode d'exécution. Comme discuté dans le thread pointé par pifor, le problème est également plus complexe qu'il ne parait car les valeurs min/max stockées dans l'index brin ne sont pas maintenues, et il n'y a aucune garanties que ces valeurs soient toujours visible, ou simplement visible par la requête en cours d'exécution. Cela veut dire la nécessité d'avoir une approche récursive dans le cas où les ranges choisis ne contiennent aucune valeur supposées être présentes uniquement dans ceux-ci. Dans des cas extrêmes le temps d'exécution pourrait devenir bien pire que lire toutes les données séquentiellement (potentiellement avec des workers parallèles). À noter qu'il n'y a pour l'instant aucune statistique qui pourrait aider à décider si ce type de parcours serait intéressant ou non. Le cumul de tout ça explique probablement pourquoi personne n'est intéressé pour travailler sur le sujet.
Julien.
https://rjuju.github.io/
Hors ligne
Pages : 1