PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Langsame Abfragereihenfolge nach einer Spalte in einer verknüpften Tabelle

Testfall

PostgreSQL 9.1. Testdatenbank mit begrenzten Ressourcen, aber ausreichend für diesen kleinen Fall. Das Gebietsschema für die Sortierung ist relevant:

SHOW LC_COLLATE;

 de_AT.UTF-8

Schritt 1) ​​Rekonstruieren Sie die Rohtestumgebung

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

Schritt 2) ANALYSE

    ANALYZE x.product;
    ANALYZE x.django_site;

Schritt 3) Nach dem Zufallsprinzip neu anordnen ()

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

ANALYZE x.p;

Ergebnisse

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 ANALYZE (-> Bitmap Index Scan)
2) Post ANALYZE (-> Seq Scan)
3) Reorder by random(), ANALYZE

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

1) Gesamtlaufzeit:1253,543 ms
2) Gesamtlaufzeit:1250,351 ms
3) Gesamtlaufzeit:1283,111 ms

ORDER  BY p.ordering, p.name

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

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

1) Gesamtlaufzeit:176.628 ms
2) Gesamtlaufzeit:176.811 ms
3) Gesamtlaufzeit:178.150 ms
Der Planer berücksichtigt natürlich diese d.id ist funktional abhängig.

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

1) Gesamtlaufzeit:242,218 ms -- !!
2) Gesamtlaufzeit:245,234 ms
3) Gesamtlaufzeit:254,581 ms
Der Planer vermisst offensichtlich diesen d.int_col (NOT NULL) ist genauso funktional abhängig. Aber das Sortieren nach einer Integer-Spalte ist billig.

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

1) Gesamtlaufzeit:2259,171 ms -- !!
2) Gesamtlaufzeit:2257,650 ms
3) Gesamtlaufzeit:2258,282 ms
Sortieren nach einem (langen) varchar oder text Spalte ist teuer ...

ORDER  BY p.name COLLATE "C"

1) Gesamtlaufzeit:327,516 ms -- !!
2) Gesamtlaufzeit:325,103 ms
3) Gesamtlaufzeit:327,206 ms
... aber nicht so teuer, wenn es ohne Locale gemacht wird.

Wenn das Gebietsschema aus dem Weg geräumt ist, wird nach einem varchar sortiert Spalte ist nicht ganz aber fast so schnell. Gebietsschema "C" ist effektiv "kein Gebietsschema, nur nach Bytewert sortieren". Ich zitiere das Handbuch:

Wenn Sie möchten, dass sich das System so verhält, als hätte es keine Locale-Unterstützung, verwenden Sie den speziellen Locale-Namen C oder äquivalent POSIX.

Alles zusammengenommen hat @dvd gewählt:

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

...3) Gesamtlaufzeit:275,854 ms
Das sollte reichen.