2012-10-03 15 views
5

Mam następującą tabelą/indeksów -Postgres łączenie wielu indeksach

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

to jest kwerenda Chcę wykonać:

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 

Kiedy uruchomić kwerendę używa tylko ix_coords indeksu. Jak mogę się upewnić, że Postgres używa indeksu ix_user_id i ix_created_at również dla zapytania?

Jest to nowa tabela, w której dokonałem zbiorczego wstawienia danych produkcyjnych. Wszystkich wiersze w tabeli test: 15.069.489

Używam PostgreSQL 9.2.1 (z PostGIS) z (effective_cache_size = 2 GB). To jest mój lokalny OSX z 16 GB RAM, Core i7/2,5 GHz, bez dysku SSD.

Dodawanie wyjście EXPLAIN ANALYZE -

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:

podstawie sugestii poniżej próbowałem indeks na sznury + user_id:

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

..ale pojawia się następujący błąd:

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. 

AKTUALIZACJA:

Tak więc CREATE EXTENSION btree_gist; rozwiązało problem z indeksem mieszanki btree/gist. A teraz mój wskaźnik wygląda

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

UWAGA: btree_gist nie akceptuje DESC/ASC.

Nowy plan zapytania:

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 

Zapytanie jest lepiej niż wcześniej, prawie drugi lepiej, ale nadal nie wielki. Myślę, że to jest najlepsze, co mogę dostać? Miałem nadzieję, że gdzieś około 60-80ms. Również pobranie z zapytania order by created_at desc powoduje zgolenie kolejnych 100 ms, co oznacza, że ​​nie można użyć indeksu. W każdym razie to naprawić?

+0

Postgres wykorzystuje planer oparty na kosztach. Nawet jeśli MOŻE korzystać z indeksu, może nie być tak szybki, jak go nie używa. Możesz grać z random_page_cost i var kosztu cpu *, aby sprawdzić, czy możesz go użyć do korzystania z tych indeksów. Skorzystaj z analizy analizy , aby zobaczyć, co postanowi zrobić i jak szybko to zrobi. –

+0

Użycie indeksu zależy również od dostępnych statystyk. Ile wierszy ma faktycznie 'user_id = '3212312''? Czy wykonałeś "analizę próżni" przed tym zapytaniem (przynajmniej po wypełnieniu tabeli)? – wildplasser

+0

Aby zobaczyć, co robi, gdy indeks 'ix_coords' nie jest dostępny - czy może używać drugiego indeksu i jaki jest koszt - spróbuj' BEGIN; DROP INDEX ix_coords NA tabeli; WYJAŚNIJ ANALIZĘ the_query; ROLLBACK; '. –

Odpowiedz

5

Nie wiem, czy Pg może łączyć indeks GiST i zwykłe indeksy b-tree ze skanowaniem indeksu bitmapy, ale podejrzewam, że nie. Być może uzyskujesz najlepszy wynik, jaki możesz osiągnąć, dodając kolumnę user_id do indeksu GiST (iw konsekwencji zwiększając ją i spowalniając w przypadku innych zapytań, które nie używają user_id).

W ramach eksperymentu możesz:

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

mogącego skutkować dużym indeksie, ale może zwiększyć tę zapytanie - czy to działa. Należy pamiętać, że utrzymywanie takiego indeksu znacznie spowolni pracę w zakresie INSERT i UPDATE. Jeśli upuścisz stare ix_coords, twoje zapytania będą używać ix_coords_and_user_id, nawet jeśli nie będą filtrować na user_id, ale będą wolniejsze niż ix_coords. Utrzymanie obu sprawi, że spowolnienie w postaci INSERT i UPDATE będzie jeszcze gorsze.

Zobacz btree-gist


(zastąpiony przez edit na pytanie, które całkowicie zmienia pytanie, kiedy napisał użytkownik miał wielokolumnowego indeks oni teraz podzielona na dwie odrębne te):

Wygląda na to, że nie filtrujesz ani nie sortujesz na user_id, tylko create_date. Pg nie będzie (nie może?) Używać tylko drugiego terminu wielokolumnowego indeksu, takiego jak (user_id, create_date), potrzebuje również użycia pierwszego elementu. Aby uzyskać indeks , należy utworzyć osobny indeks dla tej wartości. Jeśli używasz i potrzebujesz indeksu (user_id, create_date) i nie używasz samodzielnie tylko user_id, sprawdź, czy możesz odwrócić kolejność kolumn. Alternatywnie utwórz dwa niezależne indeksy: (user_id) i (create_date). Gdy obie kolumny są potrzebne Pg może łączyć dwa niezależne indeksy przy użyciu skanowania indeksu bitmapowego.

+0

Przepraszam, miałem kilka literówek w moim pytaniu, miałem mieszany id i user_id, w zasadzie jest to po prostu "user_id". – kapso

+0

Dodałem dane wyjściowe analizy wyjaśniania. Doceniam Twoją pomoc. – kapso

+0

@ user310525 Wygląda na to, że całkowicie zmieniłeś definicje indeksu, dzieląc komponent 'id_użytkownika'' ix_created_at na nowy indeks. Czy stary był po prostu zły? A może zmieniłeś konfigurację i nie wyjaśniłeś tego? Jeśli ją zmienisz, lepiej wytłumaczysz i dodasz nowy materiał, a nie tylko po cichu zmienisz to, co tam jest, więc stare odpowiedzi nie mają już sensu w kontekście. –

2

myślę Craig jest poprawny z jego odpowiedzi, ale chciałem tylko dodać kilka rzeczy (i nie pasuje w komentarzu)

Trzeba pracować bardzo ciężko życie PostgreSQL w użyciu indeks. Optymalizator zapytań jest inteligentny i zdarzają się sytuacje, w których uwierzy, że sekwencyjne skanowanie tabeli będzie szybsze. Zwykle ma rację! :) Ale możesz grać z niektórymi ustawieniami (takimi jak seq_page_cost, random_page_cost, etc), z którymi możesz grać, aby spróbować zdobyć indeks. Oto link do niektórych z configurations, które możesz chcieć sprawdzić, jeśli masz wrażenie, że nie podejmuje właściwej decyzji. Ale, znowu ... moje doświadczenie jest takie, że przez większość czasu Postgres jest mądrzejszy ode mnie! :)

Mam nadzieję, że to pomoże Tobie (lub komuś w przyszłości).

Powiązane problemy