Używam PostgreSQL 9.1 na Ubuntu 12.04.
Muszę wybrać rekordy w określonym przedziale czasu: moja tabela time_limits
ma dwa timestamp
pola i jedną integer
właściwość. W mojej rzeczywistej tabeli znajdują się dodatkowe kolumny, które nie są związane z tym zapytaniem.
create table (
start_date_time timestamp,
end_date_time timestamp,
id_phi integer,
primary key(start_date_time, end_date_time,id_phi);
Ta tabela zawiera około 2 mln rekordów.
Zapytania takie jak poniższe zajmowały dużo czasu:
select * from time_limits as t
where t.id_phi=0
and t.start_date_time <= timestamp'2010-08-08 00:00:00'
and t.end_date_time >= timestamp'2010-08-08 00:05:00';
Próbowałem więc dodać kolejny indeks - odwrotność PK:
create index idx_inversed on time_limits(id_phi, start_date_time, end_date_time);
Mam wrażenie, że poprawiła się wydajność: Czas na dostęp do rekordów na środku tabeli wydaje się bardziej rozsądny: gdzieś pomiędzy 40 a 90 sekund.
Ale wciąż jest kilkadziesiąt sekund dla wartości w środku zakresu czasu. I jeszcze dwa razy, gdy celujesz w koniec tabeli (chronologicznie).
Po explain analyze
raz pierwszy próbowałem uzyskać ten plan zapytań:
Bitmap Heap Scan on time_limits (cost=4730.38..22465.32 rows=62682 width=36) (actual time=44.446..44.446 rows=0 loops=1)
Recheck Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
-> Bitmap Index Scan on idx_time_limits_phi_start_end (cost=0.00..4714.71 rows=62682 width=0) (actual time=44.437..44.437 rows=0 loops=1)
Index Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
Total runtime: 44.507 ms
Co mogę zrobić, aby zoptymalizować wyszukiwanie? Widać cały czas spędzony na skanowaniu dwóch kolumn znaczników czasu po id_phi
ustawieniu na 0
. I nie rozumiem dużego skanu (60 000 wierszy!) Znaczników czasu. Czy nie są one indeksowane według klucza podstawowego, a idx_inversed
ja dodałem?
Czy powinienem zmienić typy znaczników czasu na coś innego?
Przeczytałem trochę o indeksach GIST i GIN. Rozumiem, że mogą być bardziej wydajne w określonych warunkach dla niestandardowych typów. Czy jest to opłacalna opcja dla mojego przypadku użycia?
źródło
explain analyze
wyjściowych to czas potrzebny na zapytanie . Jeśli zapytanie zajmuje 45 sekund, dodatkowy czas spędzony jest na przesłaniu danych z bazy danych do programu uruchamiającego zapytanie. Po wszystkich 62682 wierszach i jeśli każdy wiersz jest duży (np. Ma długivarchar
lubtext
kolumny), może to wpłynąć na czas przesyłania drastycznie.rows=62682 rows
jest oszacowaniem planisty . Zapytanie zwraca 0 wierszy.(actual time=44.446..44.446 rows=0 loops=1)
Odpowiedzi:
W przypadku Postgres 9.1 lub nowszego:
W większości przypadków kolejność sortowania indeksu jest mało istotna. Postgres może skanować wstecz praktycznie tak szybko. Ale w przypadku zapytań o zakres w wielu kolumnach może to mieć ogromną różnicę. Blisko związane:
Rozważ swoje zapytanie:
Kolejność sortowania pierwszej kolumny
id_phi
w indeksie jest nieistotna. Ponieważ jest sprawdzane pod kątem równości (=
), powinno być na pierwszym miejscu. Dobrze rozumiesz. Więcej w tej pokrewnej odpowiedzi:Postgres może szybko wskoczyć
id_phi = 0
i rozważyć następujące dwie kolumny pasującego indeksu. Są one pytane o warunki zakresu odwróconej kolejności sortowania (<=
,>=
). W moim indeksie wiersze kwalifikujące są na pierwszym miejscu. Powinno być najszybszym możliwym sposobem z indeksem B-Tree 1 :start_date_time <= something
: indeks ma najwcześniejszy znacznik czasu.Powtarzaj, dopóki pierwszy rząd nie zakwalifikuje się (superszybko).
end_date_time >= something
: indeks ma najpierw najnowszą sygnaturę czasową.Kontynuuj od następnej wartości dla kolumny 2 ..
Postgres może skanować do przodu lub do tyłu. Sposób, w jaki miałeś indeks, musi odczytać wszystkie wiersze pasujące do pierwszych dwóch kolumn, a następnie odfiltrować trzeci. Przeczytaj rozdział Indeksy i
ORDER BY
instrukcję. Całkiem dobrze pasuje do twojego pytania.Ile wierszy pasuje do pierwszych dwóch kolumn?
Tylko nieliczne z
start_date_time
początkiem przedziału czasowego tabeli. Ale prawie wszystkie rzędyid_phi = 0
na chronologicznym końcu tabeli! Tak więc wydajność pogarsza się z późniejszymi czasami uruchamiania.Szacunki planisty
Planista szacuje
rows=62682
dla przykładowego zapytania. Spośród nich żaden się nie kwalifikuje (rows=0
). Możesz uzyskać lepsze oszacowania, jeśli zwiększysz docelowy poziom statystyki dla tabeli. Dla 2.000.000 wierszy ...... może zapłacić. Lub nawet wyżej. Więcej w tej pokrewnej odpowiedzi:
Myślę, że nie potrzebujesz tego
id_phi
(tylko dla kilku odrębnych wartości, równomiernie rozmieszczonych), ale dla znaczników czasu (wiele różnych wartości, nierównomiernie rozmieszczonych).Nie sądzę też, żeby miało to znaczenie dla ulepszonego indeksu.
CLUSTER
/ pg_repackJeśli chcesz tego szybciej, możesz usprawnić fizyczną kolejność wierszy w tabeli. Jeśli możesz sobie pozwolić na zablokowanie tabeli wyłącznie na krótki czas (na przykład poza godzinami pracy), aby przepisać tabelę i uporządkować wiersze zgodnie z indeksem:
Przy równoczesnym dostępie rozważ pg_repack , który może zrobić to samo bez wyłącznej blokady.
Tak czy inaczej, efektem jest to, że mniej tabel musi być odczytanych z tabeli i wszystko jest wstępnie posortowane. Jest to jednorazowy efekt pogarszający się z czasem, gdy zapisy na stole fragmentują fizyczny porządek sortowania.
Indeks GiST w Postgresie 9.2+
1 W przypadku pg 9.2+ istnieje inna, prawdopodobnie szybsza opcja: indeks GiST dla kolumny zakresu.
Istnieją wbudowane typy zakresów dla
timestamp
itimestamp with time zone
:tsrange
,tstzrange
. Indeks btree jest zwykle szybszy dla dodatkowejinteger
kolumny, takiej jakid_phi
. Mniejszy i tańszy w utrzymaniu. Ale zapytanie będzie prawdopodobnie ogólnie szybsze z połączonym indeksem.Zmień definicję tabeli lub użyj indeksu wyrażeń .
W przypadku dostępnego wielokolumnowego indeksu GiST należy również
btree_gist
zainstalować dodatkowy moduł (jeden raz na bazę danych), który zapewnia klasy operatorów do włączeniainteger
.Trifecta! Wielokolumnowa funkcjonalnego indeksu Gist- :
W zapytaniu użyj operatora „zawiera zakres”
@>
:Indeks SP-GiST w Postgres 9.3+
SP-GIST wskaźnik może być jeszcze szybciej do tego rodzaju zapytania - chyba że, cytując instrukcję :
Nadal jest to prawda w Postgres 12.
Musisz połączyć
spgist
indeks tylko(tsrange(...))
z drugimbtree
indeksem(id_phi)
. Po dodaniu kosztów ogólnych nie jestem pewien, czy to może konkurować.Powiązana odpowiedź z testem porównawczym tylko dla
tsrange
kolumny:źródło
Odpowiedź Erwina jest już wyczerpująca, jednak:
Typy zakresów znaczników czasu są dostępne w PostgreSQL 9.1 z rozszerzeniem Temporal autorstwa Jeffa Davisa: https://github.com/jeff-davis/PostgreSQL-Temporal
Uwaga: ma ograniczone funkcje (używa Timestamptz, a afaik może nakładać się tylko na styl „[)”). Ponadto istnieje wiele innych wspaniałych powodów do uaktualnienia do PostgreSQL 9.2.
źródło
Możesz spróbować utworzyć indeks wielokolumnowy w innej kolejności:
Kiedyś zamieściłem podobne pytanie związane również z porządkowaniem indeksów w indeksie wielokolumnowym. Klucz próbuje użyć najbardziej restrykcyjnych warunków w celu zmniejszenia przestrzeni wyszukiwania.
Edycja : Mój błąd. Teraz widzę, że masz już ten indeks zdefiniowany.
źródło
Bitmap Index Scan on idx_time_limits_phi_start_end
Udało mi się szybko zwiększyć (z 1 sekundy do 70 ms)
Mam tabelę z agregacjami wielu pomiarów i wielu poziomów (
l
kolumna) (30s, 1m, 1h itp.) Istnieją dwie kolumny związane z zakresem:$s
na początek i$e
na koniec.Utworzyłem dwa indeksy wielokolumnowe: jeden dla początku i jeden dla końca.
Dostosowałem zapytanie: wybierz zakresy, w których ich początkowa granica znajduje się w danym zakresie. dodatkowo wybierz zakresy, w których ich koniec jest w danym zakresie.
Wyjaśnienie pokazuje dwa strumienie wierszy skutecznie wykorzystujących nasze indeksy.
Indeksy:
Wybierz zapytanie:
Wyjaśnić:
Sztuka polega na tym, że twoje węzły planu zawierają tylko pożądane wiersze. Wcześniej mieliśmy tysiące wierszy w węźle planu, ponieważ został on wybrany
all points from some point in time to the very end
, a następnie następny węzeł usunął niepotrzebne wiersze.źródło