Question

j'ai une table items avec le schéma suivant (dans postgres v9.3.5) :

  Column   | Type   |                         Modifiers                  | Storage  
-----------+--------+----------------------------------------------------+----------
 id        | bigint | not null default nextval('items_id_seq'::regclass) | plain    
 data      | text   | not null                                           | extended 
 object_id | bigint | not null                                           | plain    
Indexes:
    "items_pkey" PRIMARY KEY, btree (id)
    "items_object_id_idx" btree (object_id)
Has OIDs: no

Lorsque j'exécute une requête, elle se bloque pendant très longtemps :

SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1;

Après VACUUM ANALYZE, l’exécution des requêtes s’est beaucoup améliorée, mais toujours pas parfaite.

# EXPLAIN ANALYZE SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1;
                                                                            QUERY PLAN                                  
------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.44..1269.14 rows=1 width=63) (actual time=873796.061..873796.061 rows=0 loops=1)
   ->  Index Scan Backward using items_pkey on items  (cost=0.44..1164670.11 rows=918 width=63) (actual time=873796.059..873796.059 rows=0 loops=1)
         Filter: (object_id = 123::bigint)
         Rows Removed by Filter: 27942522
 Total runtime: 873796.113 ms
(5 rows)

Ce qui est étrange, c'est que lorsque j'exécute

SELECT * FROM "items" WHERE "object_id" = '123' LIMIT 1;

il renvoie 0 ligne et je peux le faire dans mon code pour optimiser les performances de mon application Web, mais pourquoi cela peut-il être fait par Postgres lui-même ?Je suis arrivé à Postgres depuis MySQL et je n'y ai jamais vu de choses aussi étranges.

=====

J'ai constaté qu'il utilise un plan de requête différent, un index différent, mais pourquoi ?

# EXPLAIN ANALYZE SELECT * FROM "items" WHERE "object_id" = '123' LIMIT 1;
                                                                          QUERY PLAN                                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.56..3.34 rows=1 width=63) (actual time=0.014..0.014 rows=0 loops=1)
   ->  Index Scan using items_object_id_operation_idx on items  (cost=0.56..2579.16 rows=929 width=63) (actual time=0.013..0.013 rows=0 loops=1)
         Index Cond: (object_id = 123::bigint)
 Total runtime: 0.029 ms
(4 rows)
Était-ce utile?

La solution 2

Pour optimiser la requête

SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1;

j'ai fait suivre

SELECT * FROM 
    (SELECT * FROM "items" 
     WHERE "object_id" = '123'
     ORDER BY "id" DESC) AS "items" 
ORDER BY "id" DESC LIMIT 1;

Cela a aidé sans ajouter d'index (object_id asc, id desc) ce qui a été suggéré par @mustaccio.

# EXPLAIN SELECT * FROM 
    (SELECT * FROM "items" 
     WHERE "object_id" = '123'
     ORDER BY "id" DESC) AS "items" 
ORDER BY "id" DESC LIMIT 1;
                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Limit  (cost=16629.84..16629.86 rows=1 width=59)
   ->  Sort  (cost=16629.84..16640.44 rows=4239 width=59)
         Sort Key: items.id
         ->  Bitmap Heap Scan on items  (cost=125.42..16374.45 rows=4239 width=59)
               Recheck Cond: (object_id = 123::bigint)
                   ->  Bitmap Index Scan on items_object_id_idx  (cost=0.00..124.36 rows=4239 width=0)
                     Index Cond: (object_id = 123::bigint)
(7 rows)

Autres conseils

Essayer d'expliquer pourquoi il existe une différence de performances entre les deux requêtes.

Celui-ci: SELECT * FROM "items" WHERE "object_id" = '123' LIMIT 1 est satisfait par n'importe lequel une ligne avec la correspondance object_id, donc l'index sur object_id est un choix naturel.La requête nécessite un minimum d'E/S :analyse d'index pour trouver la première valeur correspondante plus une lecture de tas pour récupérer la ligne entière.

L'alternative: SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1 a besoin tous lignes avec la correspondance object_id être trié par une autre colonne, id, puis la ligne avec la valeur maximale de id être retourné.Si vous deviez utiliser l'index sur object_id vous devrez effectuer les opérations suivantes :parcourez l'index pour trouver chaque correspondant à object_id;pour chaque correspondance, allez chercher la ligne réelle ;puis triez toutes les lignes récupérées par id et renvoie celui avec le plus grand id.

L'alternative choisie par l'optimiseur, vraisemblablement basée sur la object_id histogramme, est :scanner l'index sur id à l'envers, dans son intégralité ;pour chaque valeur, allez chercher la ligne et vérifiez si la valeur de object_id allumettes;renvoie la première ligne correspondante, qui aura le maximum possible id valeur.Cette alternative évite de trier les lignes, donc je suppose que l'optimiseur la préfère à l'utilisation de l'index sur object_id.

La présence d'un index sur (object_id asc, id desc) permet encore une autre alternative :scannez ce nouvel index pour la première entrée correspondant au fourni object_id valeur, qui par définition aura la valeur la plus élevée id valeur;allez chercher une ligne correspondante et revenez.C’est évidemment l’approche la plus efficace.

Licencié sous: CC-BY-SA avec attribution
Non affilié à dba.stackexchange
scroll top