Ordre des colonnes dans un index composé dans PostgreSQL (et ordre des requêtes)

9

J'ai une table avec 50K lignes. Il s'agit en fait d'une table PostGIS.

La requête comprend 4 parties (1 obligatoire) (3 facultatives)

  1. boîte d'intersection (un rectangle de géographie) avec 4 lat, longue (j'utilise st_intersects) [Obligatoire]
  2. Plage de dates (min, max) sur un champ de date
  3. Type de fichier (un ensemble de jusqu'à 8 valeurs de texte) utilisant actuellement IN (.....) mais je peux en faire une table temporaire si nécessaire. Je vois que beaucoup de gens n'aiment pas IN.
  4. Pays (une valeur de texte).

J'attends environ 100 à 4 000 lignes renvoyées

Si je crée un index composé sur la table, quelle colonne dois-je utiliser en premier. Le grain fin est probablement l'emplacement (les données sont réparties dans le monde entier). Je l'ai actuellement comme index GIST.

Les autres indices seraient BTREE.

Mon intuition dit d'utiliser du grain fin, et bien sûr durer. Par exemple, il n'y a qu'environ 12 types de fichiers, ce qui constituerait de très gros compartiments pour l'index.

Que disent les gourous PostgreSQL et PostGIS (qui connaissent les composants internes du système)?


MISE À JOUR:

Permettez-moi d'affiner cette question.

  1. Je ne veux pas que quiconque doive faire le travail que je devrais faire. Je respecte trop votre temps. Je vais donc passer à l'analyse d'expliquer plus tard.
  2. Tout ce que je cherchais, c'était des pointeurs, des conseils et des directives.
  3. J'ai lu cette excellente petite publication: https://devcenter.heroku.com/articles/postgresql-indexes#managing-and-maintaining-indexes sur les index
  4. Ce que je fais normalement, c'est créer 4 index distincts (boîte géographique, nom de pays, type_fichier et date), mais ce que je veux voir ce que ferait une requête composite.

Dites-moi si l'une de ces hypothèses est fausse. (Je suis assez nouveau dans l'idée des indices composés)

  1. L'ordre est important. Choisissez comme premier index celui qui coupera le plus les lignes (dans mon cas, l'emplacement (géographie) qui est un simple polygone ou multi-polygone ferait le mieux).
  2. Parfois, les requêtes ignorent les index. Mais si je crée une requête composée avec clé (# 1, # 2, # 3, # 4), même si l'utilisateur crée quelque chose qui demande # 1, # 3, le planificateur utilisera toujours la seule requête composite, car il commande est maintenu.
  3. Normalement, je créerais trois requêtes BTREE et une GIST (pour le type geography). PostGIS ne prend pas en charge la création d'un composé à partir de plusieurs types d'index. Je vais donc devoir utiliser GIST l'indice composé. Mais cela ne devrait pas blesser les choses.
  4. Si je crée des index composés ou à valeur unique supplémentaires, le planificateur est assez intelligent pour choisir le plus intelligent .....
  5. Le nom du pays peut avoir environ 250 valeurs différentes et est évidemment fortement lié à l'emplacement (géobox), mais si le prochain meilleur index pour réduire la taille de la ligne est file_type, je devrais l'utiliser ensuite. Je ne m'attends pas à ce que les utilisateurs utilisent souvent le pays ou la date dans leurs ensembles de requêtes.
  6. Je n'ai PAS à vous soucier de créer un index composé de 4 clés augmentera considérablement la taille des données d'index. Autrement dit, si un indice à une clé représente 90% de l'augmentation des performances, cela ne fait pas de mal d'ajouter 3 éléments supplémentaires pour le rendre composé. Inversement, je devrais vraiment créer les deux index. Un index géographique unique, ainsi qu'un index composé, et laissez le planificateur déterminer ce qui est le mieux, et il prendra en compte la taille de la table d'index.

Encore une fois, je ne demande à personne de concevoir ma solution, je ne me moque pas du travail des autres. Mais j'ai besoin de choses que la documentation PostGreSQL ne me dit pas sur l'implémentation

[La raison pour laquelle je n'ai pas encore de résultat EXPLAIN à montrer, c'est que je dois créer ce tableau de 25 Ko à partir d'un tableau de 24 Mo. Cela prend plus de temps que je ne le pensais. Je regroupe les choses en 1 000 groupes d'articles et laisse l'utilisateur interroger la table de lignes de 25 Ko. Mais ma prochaine question, impliquera d'utiliser les résultats de cette requête pour accéder à la table de lignes MASTER 25M et retirer les choses, et c'est là que les performances de l'indice composé vont vraiment HIT].


exemple de requête ci-dessous:


SELECT
    public.product_list_meta_mv.cntry_name       AS country,
    public.product_list_meta_mv.product_producer AS producer,
    public.product_list_meta_mv.product_name     AS prod_name,
    public.product_list_meta_mv.product_type     AS ptype,
    public.product_list_meta_mv.product_size     AS size,
    ST_AsGeoJSON(public.product_list_meta_mv.the_geom, 10, 2)          AS outline
FROM
    public.product_list_meta_mv 
WHERE
    public.product_list_meta_mv.cntry_name = 'Poland' 
AND
    ST_Intersects(public.product_list_meta_mv.the_geom,
    st_geogfromtext('SRID=4326;POLYGON((21.23107910156250 51.41601562500000,
                                        18.64379882812500 51.41601562500000,
                                        18.64379882812500 48.69415283203130,
                                        21.23107910156250 48.69415283203130,
                                        21.23107910156250 51.41601562500000))')) 
AND (date >= '1/2/1900 5:00:00 AM' 
 AND date <= '2/26/2014 10:26:44 PM')
AND (public.product_list_meta_mv.product_type in
    ('CIB10','DTED0','DTED1','DTED2','CIB01','CIB05')) ;

EXPLIQUEZ LES RÉSULTATS DE L'ANALYSE (je n'ai pas mis d'index composés, et à partir de la vitesse que je vois, je ne sais pas si j'en ai besoin).

"Bitmap Heap Scan on catalog_full cat  (cost=4.33..37.49 rows=1 width=7428) (actual time=1.147..38.051 rows=35 loops=1)"
"  Recheck Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"  Filter: (((type)::text = ANY ('{CADRG,CIB10,DTED1,DTED2}'::text[])) AND (_st_distance('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography, outline, 0::double precision, false) < 1e-005::double precision))"
"  Rows Removed by Filter: 61"
"  ->  Bitmap Index Scan on catalog_full_outline_idx  (cost=0.00..4.33 rows=8 width=0) (actual time=0.401..0.401 rows=96 loops=1)"
"        Index Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"Total runtime: 38.109 ms"

EXPLAIN ANALYZE SELECT pid,product_name,type,country,date,size,cocom,description,egpl_date,ST_AsGeoJSON(outline, 10, 2) AS outline 
FROM portal.catalog_full AS cat 
WHERE ST_Intersects(st_geogfromtext('SRID=4326;POLYGON((21.2200927734375 51.38031005859375, 18.65478515625 51.38031005859375, 18.65478515625 48.7298583984375, 21.2200927734375 48.7298583984375, 21.2200927734375 51.38031005859375))'), cat.outline) 
AND (cat.type in ('CADRG','CIB10','DTED1','DTED2'))
Dr.YSG
la source
2
Veuillez fournir la requête réelle, s'il vous plaît.
ypercubeᵀᴹ
Le "3 optionnel" signifie-t-il que la requête peut avoir 8 variantes différentes (selon que les options 2,3,4 sont activées ou non)?
ypercubeᵀᴹ
Il y a 4 composants ET dans le WHERE. Sur les st_intersectes est requis, les autres peuvent être là ou non. Mais je veux parler du cas où ils sont tous présents.
2
J'ai voté pour migrer la question vers dba.se, il s'agit d'une requête complexe avec plusieurs conditions de plage.
ypercubeᵀᴹ
1
Afficher EXPLAIN ANALYZEpour la requête.
Craig Ringer

Réponses:

4

Dans le cadre de mon travail, je gère une base de données PostgreSQL assez volumineuse (environ 120 Go sur le disque, plusieurs tables de plusieurs millions de lignes) et j'ai rassemblé quelques astuces pour accélérer les requêtes. D'abord quelques commentaires sur vos hypothèses:

  1. Oui, l'ordre est important, mais ce n'est que le premier qui est vraiment différent, les autres sont des index de seconde classe.
  2. Je ne suis pas sûr qu'il utilisera toujours les deux, je suppose que le planificateur de requêtes utilisera # 1, puis fera quelque chose d'intelligent avec le reste.
  3. Je n'ai aucune expérience avec GIST.
  4. Oui, ajoutez d'abord tous les index, voyez ce qui est le plus utilisé et ce qui donne les meilleures performances.
  5. Je vous suggère d'essayer les deux et de mesurer ce qui fonctionne le mieux. Essayez de réécrire le sql avec différentes sous-requêtes, peut-être le pays et l'heure en une, puis rejoignez la requête d'intersection. Je n'ai remarqué aucun problème de performance avec les clauses IN, tant que la liste IN ne fait pas des milliers d'éléments. Je suppose que quelques requêtes différentes spécifiquement ajustées en fonction des critères de saisie disponibles donneront les meilleurs résultats.
  6. Je recommanderais de ne pas créer un index à 4 voies. Essayez d'en créer un, puis vérifiez la taille, ils peuvent devenir vraiment énormes. D'après mon expérience, quatre index à 1 clé ont été presque aussi rapides qu'un index à 4 voies. Une astuce qui fonctionne bien pour certaines requêtes spécifiques sont les index partiels, c'est-à-dire quelque chose comme ceci:

    CRÉER INDEX SUR table_x (clé1, clé2, clé3) OERE some_x_column = 'XXXX';

J'ai créé des alias dans mon fichier .psqlrc avec des requêtes pour aider à trouver les index à ajouter ou à supprimer. N'hésitez pas à les consulter sur GitHub: .psql

J'utilise beaucoup: seq_scans et: bigtables, puis \ d nom_table pour obtenir des détails sur la table. N'oubliez pas de réinitialiser les statistiques après avoir effectué quelques modifications, sélectionnez pg_stat_reset ();

Claes Mogren
la source
1
Ce sont d'excellents conseils. J'ai suivi votre conseil, puis je l'ai utilisé pour faire une expérience sur une table beaucoup plus grande que nous maintenons (43 millions de lignes). Les résultats sont à: dba.stackexchange.com/questions/61084/…
Dr.YSG
1

Je pense que la chose la plus susceptible d'aider (le cas échéant) serait d'ajouter product_type en tant que 2e colonne à l'index gist. Mais sans savoir combien de lignes correspondent à chacune des conditions ET (isolément) pour vos requêtes typiques / problématiques, nous ne pouvons que deviner.

Lorsque j'aborde cette question, la première chose que je fais est d'exécuter la requête sous une forme simplifiée où la clause WHERE n'a qu'une seule condition, chacune prise à son tour, sous EXPLAIN ANALYZE. Regardez à la fois les lignes estimées et les lignes réelles pour chacune.

jjanes
la source
voir ma mise à jour ci-dessus, mais je pense que vous me donnez une bonne avance, pensez à ordonner les index qui réduisent le plus rapidement la sortie des lignes. Est-ce correct?
Dr.YSG