web-dev-qa-db-fra.com

les performances de postgres_fdw sont lentes

La requête suivante sur un étranger prend environ 5 secondes pour s'exécuter sur 3,2 millions de lignes:

SELECT x."IncidentTypeCode", COUNT(x."IncidentTypeCode") 
FROM "IntterraNearRealTimeUnitReflexes300sForeign" x 
WHERE x."IncidentDateTime" >= '05/01/2016' 
GROUP BY x."IncidentTypeCode" 
ORDER BY 1;

Lorsque j'exécute la même requête sur une table normale, elle revient en 0,6 seconde. Les plans d'exécution sont assez différents:

Table normale

Sort  (cost=226861.20..226861.21 rows=4 width=4) (actual time=646.447..646.448 rows=7 loops=1) 
  Sort Key: "IncidentTypeCode" 
  Sort Method: quicksort  Memory: 25kB 
  -> HashAggregate (cost=226861.12..226861.16 rows=4 width=4) (actual  time=646.433..646.434 rows=7 loops=1)
     Group Key: "IncidentTypeCode"
     -> Bitmap Heap Scan on "IntterraNearRealTimeUnitReflexes300s" x  (cost=10597.63..223318.41 rows=708542 width=4) (actual time=74.593..342.110 rows=709376 loops=1) 
        Recheck Cond: ("IncidentDateTime" >= '2016-05-01 00:00:00'::timestamp without time zone) 
        Rows Removed by Index Recheck: 12259 
        Heap Blocks: exact=27052 lossy=26888
        -> Bitmap Index Scan on idx_incident_date_time_300  (cost=0.00..10420.49 rows=708542 width=0) (actual time=69.722..69.722 rows=709376 loops=1) 
           Index Cond: ("IncidentDateTime" >= '2016-05-01 00:00:00'::timestamp without time zone) 

Planning time: 0.165 ms 
Execution time: 646.512 ms

Table étrangère

Sort  (cost=241132.04..241132.05 rows=4 width=4) (actual time=4782.110..4782.112 rows=7 loops=1)   
  Sort Key: "IncidentTypeCode" 
  Sort Method: quicksort  Memory: 25kB
  -> HashAggregate  (cost=241131.96..241132.00 rows=4 width=4) (actual time=4782.097..4782.100 rows=7 loops=1)
     Group Key: "IncidentTypeCode"
     -> Foreign Scan on "IntterraNearRealTimeUnitReflexes300sForeign" x  (cost=10697.63..237589.25 rows=708542 width=4) (actual time=1.916..4476.946 rows=709376 loops=1) 

Planning time: 1.413 ms 
Execution time: 4782.660 ms

Je pense que je paie un prix élevé pour le GROUP BY clause, qui n'est pas transmise au serveur étranger lorsque je EXPLAIN VERBOSE:

SELECT
    "IncidentTypeCode"
FROM
    PUBLIC ."IntterraNearRealTimeUnitReflexes300s"
WHERE
    (
        (
            "IncidentDateTime" >= '2016-05-01 00:00:00' :: TIMESTAMP WITHOUT TIME ZONE
        )
    )

Cela renvoie 700k lignes. Y a-t-il un moyen de contourner ceci?

J'ai passé beaucoup de temps à lire cette page de documentation hier, et je pensais avoir trouvé ma réponse en définissant use_remote_estimate à vrai, mais cela n'a eu aucun effet.

J'ai accès au serveur étranger pour créer des objets si nécessaire. La valeur d'horodatage dans la clause WHERE peut être n'importe quoi; il ne provient pas d'une liste de valeurs prédéfinies.

12
J-DawG

Si tu utilises use_remote_estimate assurez-vous d'exécuter ANALYSEZ la table étrangère (je vois des estimations assez proches du retour, vous l'avez probablement fait). De plus, les améliorations du menu déroulant ne sont pas disponibles dans la version <9.5. Je suppose également que vous avez la même structure de table sur le serveur distant (y compris les index). Si un bitmap est nécessaire en raison de la faible cardinalité, il n'utilisera pas l'index en raison des limitations du mécanisme de refoulement. Vous souhaiterez peut-être réduire le nombre de lignes renvoyées pour forcer une analyse d'index BTREE (plages d'horodatage). Malheureusement, il n'existe aucun moyen propre d'éviter le SeqScan sur le serveur distant si le filtre renvoie + 10% des lignes de la table (peut varier ce pourcentage si le planificateur considère que l'analyse de la table entière est moins chère que la recherche de lectures). Si vous utilisez SSD, vous trouverez probablement utile de Tweak random_page_cost).

Vous pouvez utiliser CTE pour isoler le comportement GROUP BY:

WITH atable AS (
    SELECT "IncidentTypeCode"
    FROM PUBLIC ."IntterraNearRealTimeUnitReflexes300s"
    WHERE 
       ("IncidentDateTime" 
              BETWEEN '2016-05-01 00:00:00'::TIMESTAMP WITHOUT TIME ZONE 
                  AND '2016-05-02 00:00:00'::TIMESTAMP WITHOUT TIME ZONE)
)
SELECT atable."IncidentTypeCode", COUNT(atable.IncidentTypeCode) 
FROM atable
GROUP BY atable."IncidentTypeCode" 
ORDER BY atable."IncidentTypeCode";
7
3manuek