PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Tri lent des requêtes par colonne dans une table jointe

Cas de test

PostgreSQL 9.1. Testez la base de données avec des ressources limitées, mais bien suffisantes pour ce petit boîtier. Les paramètres régionaux de classement seront pertinents :

SHOW LC_COLLATE;

 de_AT.UTF-8

Étape 1) Reconstruire l'environnement de test brut

-- DROP TABLE x;
CREATE SCHEMA x;  -- test schema

-- DROP TABLE x.django_site;
CREATE TABLE x.django_site (
id serial primary key
,domain character varying(100) not null
,int_col int not null
);
INSERT INTO x.django_site values (1,'www.testsite.com/foodir/', 3);

-- DROP TABLE x.product;
CREATE TABLE x.product (
 id serial primary key
,site_id integer not null
,name character varying(255) not null
,slug character varying(255) not null
,sku character varying(255) 
,ordering integer not null
,active boolean not null
);

INSERT INTO x.product (site_id, name, slug, sku, ordering, active)
SELECT 1
    ,repeat(chr((random() * 255)::int + 32), (random()*255)::int)
    ,repeat(chr((random() * 255)::int + 32), (random()*255)::int)
    ,repeat(chr((random() * 255)::int + 32), (random()*255)::int)
    ,i -- ordering in sequence
    ,NOT (random()* 0.5174346569119122)::int::bool
FROM generate_series(1, 17540) AS x(i);
-- SELECT ((591::float8 / 17540)* 0.5) / (1 - (591::float8 / 17540))
-- = 0.5174346569119122

CREATE INDEX product_site_id on x.product(site_id);

Étape 2) ANALYSER

    ANALYZE x.product;
    ANALYZE x.django_site;

Étape 3) Réorganiser PAR random()

-- DROP TABLE x.p;
CREATE TABLE x.p AS
SELECT *
FROM   x.product
ORDER  BY random();

ANALYZE x.p;

Résultats

EXPLAIN ANALYZE
    SELECT p.*
    FROM   x.p
    JOIN   x.django_site d ON (p.site_id = d.id)
    WHERE  p.active
    AND    p.site_id = 1
--    ORDER  BY d.domain, p.ordering, p.name
--    ORDER  BY p.ordering, p.name
--    ORDER  BY d.id, p.ordering, p.name
--    ORDER  BY d.int_col, p.ordering, p.name
--    ORDER  BY p.name COLLATE "C"
--    ORDER  BY d.domain COLLATE "C", p.ordering, p.name -- dvd's final solution

1) Pré-ANALYSE (-> scan d'index bitmap)
2) Post-ANALYSE (-> seq scan)
3) Réorganisation par random(), ANALYZE

ORDER  BY d.domain, p.ordering, p.name

1) Autonomie totale :1 253,543 ms
2) Autonomie totale :1 250,351 ms
3) Autonomie totale :1 283,111 ms

ORDER  BY p.ordering, p.name

1) Autonomie totale :177,266 ms
2) Autonomie totale :174,556 ms
3) Autonomie totale :177,797 ms

ORDER  BY d.id, p.ordering, p.name

1) Durée d'exécution totale :176,628 ms
2) Durée d'exécution totale :176,811 ms
3) Durée d'exécution totale :178,150 ms
Le planificateur tient évidemment compte de ce d.id est fonctionnellement dépendant.

ORDER  BY d.int_col, p.ordering, p.name -- integer column in other table

1) Durée d'exécution totale :242,218 ms -- !!
2) Durée d'exécution totale :245,234 ms
3) Durée d'exécution totale :254,581 ms
Le planificateur manque évidemment ce d.int_col (NOT NULL) est tout aussi fonctionnellement dépendant. Mais trier par une colonne entière est bon marché.

ORDER  BY p.name -- varchar(255) in same table

1) Durée d'exécution totale :2 259,171 ms -- !!
2) Durée d'exécution totale :2 257,650 ms
3) Durée d'exécution totale :2 258,282 ms
Trier par un (long) varchar ou text la colonne est chère...

ORDER  BY p.name COLLATE "C"

1) Autonomie totale :327,516 ms -- !!
2) Autonomie totale :325,103 ms
3) Autonomie totale :327,206 ms
... mais moins cher si fait sans paramètres régionaux.

Avec les paramètres régionaux à l'écart, trier par un varchar colonne n'est pas tout à fait mais presque aussi rapide. Paramètres régionaux "C" est effectivement "pas de paramètres régionaux, juste un ordre par valeur d'octet". Je cite le manuel :

Si vous voulez que le système se comporte comme s'il n'avait pas de prise en charge des paramètres régionaux, utilisez le nom de paramètre régional spécial C, ou de manière équivalente POSIX.

En mettant tout cela ensemble, @dvd a choisi :

ORDER  BY d.domain COLLATE "C", p.ordering, p.name

...3) Durée d'exécution totale :275,854 ms
Cela devrait suffire.