2012-10-03 2 views
5

Ich habe folgende Tabelle/Indizes kombinieren -Postgres mehrere Indizes

CREATE TABLE test 
(
    coords geography(Point,4326), 
    user_id varchar(50), 
    created_at timestamp 
); 
CREATE INDEX ix_coords ON test USING GIST (coords); 
CREATE INDEX ix_user_id ON test (user_id); 
CREATE INDEX ix_created_at ON test (created_at DESC); 

Dies ist die Abfrage, die ich ausführen wollen:

select * 
from updates 
where ST_DWithin(coords, ST_MakePoint(-126.4, 45.32)::geography, 30000) 
and user_id='3212312' 
order by created_at desc 
limit 60 

Wenn ich die Abfrage ausführen verwendet es nur ix_coords Index. Wie kann ich sicherstellen, dass Postgres und ix_created_at Index für die Abfrage verwendet?

Dies ist eine neue Tabelle, in der ich Massenproduktionsdaten eingefügt habe. Insgesamt Zeilen in der Tabelle test: 15.069.489

Ich bin mit PostgreSQL 9.2.1 (mit Postgis) mit (effective_cache_size = 2GB). Dies ist mein lokaler OSX mit 16 GB RAM, Core i7/2.5 GHz, Nicht-SSD-Festplatte.

Hinzufügen der EXPLAIN ANALYZE Ausgabe -

Limit (cost=71.64..71.65 rows=1 width=280) (actual time=1278.652..1278.665 rows=60 loops=1) 
    -> Sort (cost=71.64..71.65 rows=1 width=280) (actual time=1278.651..1278.662 rows=60 loops=1) 
     Sort Key: created_at 
     Sort Method: top-N heapsort Memory: 33kB 
     -> Index Scan using ix_coords on test (cost=0.00..71.63 rows=1 width=280) (actual time=0.198..1278.227 rows=178 loops=1) 
       Index Cond: (coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography) 
       Filter: (((user_id)::text = '4f1092000b921a000100015c'::text) AND ('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true)) 
       Rows Removed by Filter: 3122459 
Total runtime: 1278.701 ms 

UPDATE:

Auf der Grundlage der Vorschläge unten I Index versucht, auf Schnüre + user_id:

CREATE INDEX ix_coords_and_user_id ON updates USING GIST (coords, user_id); 

..aber die folgende Fehlermeldung zu erhalten:

ERROR: data type character varying has no default operator class for access method "gist" 
HINT: You must specify an operator class for the index or define a default operator class for the data type. 

UPDATE:

So löste die CREATE EXTENSION btree_gist; den btree/gist Verbund Index Problem. Und jetzt sieht mein Index wie

CREATE INDEX ix_coords_user_id_created_at ON test USING GIST (coords, user_id, created_at); 

HINWEIS: btree_gist akzeptiert nicht DESC/ASC.

Neuer Abfrage-Plan:

Limit (cost=134.99..135.00 rows=1 width=280) (actual time=273.282..273.292 rows=60 loops=1) 
    -> Sort (cost=134.99..135.00 rows=1 width=280) (actual time=273.281..273.285 rows=60 loops=1) 
     Sort Key: created_at 
     Sort Method: quicksort Memory: 41kB 
     -> Index Scan using ix_updates_coords_user_id_created_at on updates (cost=0.00..134.98 rows=1 width=280) (actual time=0.406..273.110 rows=115 loops=1) 
       Index Cond: ((coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography) AND ((user_id)::text = '4e952bb5b9a77200010019ad'::text)) 
       Filter: (('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true)) 
       Rows Removed by Filter: 1 
Total runtime: 273.331 ms 

Die Abfrage durchführen wird, besser als zuvor, fast eine Sekunde besser, aber immer noch nicht groß. Ich denke, das ist das Beste, was ich bekommen kann ?? Ich hoffte irgendwo um 60-80ms. Wenn Sie aus der Abfrage entfernen, werden weitere 100ms entfernt, was bedeutet, dass der Index nicht verwendet werden kann. Wie auch immer, um das zu beheben?

+0

Postgres verwendet einen kostenbasierten Planer. Auch wenn es den Index verwenden kann, ist es möglicherweise nicht so schnell wie es nicht verwendet wird. Sie können mit random_page_cost und cpu * cost vars spielen, um zu sehen, ob Sie mit diesen Indizes sprechen können. Verwenden Sie explain analyze , um zu sehen, was es zu tun hat und wie schnell es ist. –

+0

Die Verwendung eines Index hängt auch von den verfügbaren Statistiken ab. Wie viele Zeilen haben eigentlich 'user_id =' 3212312''? Haben Sie vor dieser Abfrage eine "Vakuumanalyse" durchgeführt (zumindest nach dem Auffüllen der Tabelle)? – wildplasser

+0

Um zu sehen, was es macht, wenn der 'ix_coords' Index nicht verfügbar ist - ob es den anderen Index benutzen kann und wie hoch die Kosten sind - probiere' BEGIN; DROP INDEX ix_coords ON die Tabelle; EXPLAIN ANALYSE the_query; ROLLBACK; '. –

Antwort

5

Ich weiß nicht, ob Pg einen GiST-Index und regulären B-Baum-Indizes mit einem Bitmap-Index-Scan kombinieren kann, aber ich vermute nicht. Sie erhalten möglicherweise das bestmögliche Ergebnis, ohne eine user_id-Spalte zu Ihrem GiST-Index hinzuzufügen (und sie daher für andere Abfragen, die user_id nicht verwenden, größer und langsamer zu machen).

Als Experiment konnte man:

CREATE EXTENSION btree_gist; 
CREATE INDEX ix_coords_and_user_id ON test USING GIST (coords, user_id); 

, die in einem großen Index führen dürfte, könnte aber die Abfrage steigern - wenn es funktioniert. Beachten Sie, dass das Aufrechterhalten eines solchen Indexes und UPDATE s deutlich verlangsamen wird. Wenn Sie die alte ix_coords löschen, werden Ihre Abfragen ix_coords_and_user_id verwenden, auch wenn sie nicht auf user_id filtern, aber es wird langsamer sein als ix_coords. Beide zu halten wird die INSERT und UPDATE Verlangsamung sogar noch schlimmer machen.

Siehe btree-gist


(durch bearbeiten Obsoleted in Frage zu stellen, dass ändert sich die Frage vollständig, wenn geschrieben der Benutzer einen mehrspaltigen Index hatte sie jetzt in zwei getrennte diejenigen aufgeteilt haben):

Sie scheinen nicht zu filtern oder zu sortieren user_id, nur create_date. Pg wird nicht (kann nicht?) Nur den zweiten Ausdruck eines mehrspaltigen Index wie (user_id, create_date) verwenden, es braucht auch die Verwendung des ersten Elements.

Wenn Sie create_date indizieren möchten, erstellen Sie einen separaten Index dafür. Wenn Sie den Index (user_id, create_date) verwenden und benötigen und nicht nur user_id allein verwenden, überprüfen Sie, ob Sie die Spaltenreihenfolge umkehren können. Erstellen Sie alternativ zwei unabhängige Indizes (user_id) und (create_date). Wenn beide Spalten benötigt werden, kann Pg die beiden unabhängigen Indizes mit einem Bitmap-Index-Scan kombinieren.

+0

Entschuldigung, ich hatte einige Tippfehler in der Frage, hatte gemischte ID & user_id, im Grunde ist es nur "user_id". – kapso

+0

Ich habe die EXPLAIN-Analyse-Ausgabe hinzugefügt. Schätze deine Hilfe. – kapso

+0

@ user310525 Sie scheinen Ihre Indexdefinitionen vollständig geändert zu haben, indem Sie die 'user_id'-Komponente von' ix_created_at' in einen neuen Index aufgeteilt haben. War der alte einfach falsch? Oder hast du dein Setup geändert und das nicht erklärt? Wenn Sie es ändern, besser erklären und neues Material hinzufügen, nicht nur stillschweigend ändern, was dort ist, so machen alte Antworten im Kontext keinen Sinn mehr. –

2

Ich denke, Craig mit seiner Antwort richtig ist, aber ich wollte nur ein paar Dinge hinzufügen (und es wäre nicht in einem Kommentar passen)

Sie haben ziemlich hart PostgreSQL Kraft arbeiten zu verwenden ein Index. Der Abfrageoptimierer ist intelligent und es gibt Zeiten, in denen er glaubt, dass ein sequentieller Tabellenscan schneller ist. Es ist normalerweise richtig! :) Aber Sie können mit einigen Einstellungen (wie seq_page_cost, random_page_cost, etc.) spielen, mit denen Sie versuchen können, einen Index zu bevorzugen. Hier ist ein Link zu einigen der configurations, die Sie untersuchen möchten, wenn Sie das Gefühl haben, dass es nicht die richtige Entscheidung trifft. Aber, wieder ... meine Erfahrung ist, dass Postgres die meiste Zeit schlauer ist als ich! :)

Hoffe das hilft dir (oder jemand in der Zukunft).