PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Ordinamento lento delle query in base a una colonna in una tabella unita

Caso di prova

PostgreSQL 9.1. Testare il database con risorse limitate, ma abbastanza per questo piccolo caso. Le impostazioni locali per le regole di confronto saranno rilevanti:

SHOW LC_COLLATE;

 de_AT.UTF-8

Passaggio 1) Ricostruisci l'ambiente di test grezzo

-- 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);

Passaggio 2) ANALIZZA

    ANALYZE x.product;
    ANALYZE x.django_site;

Passaggio 3) Riordina PER casuale()

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

ANALYZE x.p;

Risultati

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) Pre ANALIZZA (-> scansione dell'indice bitmap)
2) Post ANALIZZA (-> scansione sequenziale)
3) Riordina per random(), ANALIZZA

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

1) Tempo di esecuzione totale:1253,543 ms
2) Tempo di esecuzione totale:1250,351 ms
3) Tempo di esecuzione totale:1283,111 ms

ORDER  BY p.ordering, p.name

1) Durata totale:177.266 ms
2) Durata totale:174.556 ms
3) Durata totale:177.797 ms

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

1) Durata totale:176.628 ms
2) Durata totale:176.811 ms
3) Durata totale:178.150 ms
Il pianificatore ovviamente tiene conto di questo d.id è funzionalmente dipendente.

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

1) Durata totale:242.218 ms -- !!
2) Durata totale:245.234 ms
3) Durata totale:254.581 ms
Al pianificatore ovviamente manca quel d.int_col (NON NULL) è altrettanto dipendente dal punto di vista funzionale. Ma l'ordinamento in base a una colonna intera è economico.

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

1) Durata totale:2259,171 ms -- !!
2) Durata totale:2257,650 ms
3) Durata totale:2258,282 ms
Ordinamento per varchar (lungo). o text la colonna è costosa...

ORDER  BY p.name COLLATE "C"

1) Durata totale:327.516 ms -- !!
2) Durata totale:325.103 ms
3) Durata totale:327.206 ms
... ma non così costoso se fatto senza locale.

Con le impostazioni locali fuori mano, ordinando per varchar la colonna non è del tutto ma quasi altrettanto veloce. Locale "C" è effettivamente "nessuna localizzazione, solo ordine per valore byte". Cito il manuale:

Se vuoi che il sistema si comporti come se non avesse il supporto per la localizzazione, usa il nome della locale speciale C, o equivalentemente POSIX.

Mettendo tutto insieme, @dvd ha scelto:

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

...3) Durata totale:275,854 ms
Dovrebbe andare.