Zadanie tego pytania, szczególnie postgresowi, ponieważ ma dobre wsparcie dla indeksów R / drzewa / przestrzennych.
Mamy następującą tabelę ze strukturą drzewa (model zestawu zagnieżdżonego) słów i ich częstotliwości:
lexikon
-------
_id integer PRIMARY KEY
word text
frequency integer
lset integer UNIQUE KEY
rset integer UNIQUE KEY
I zapytanie:
SELECT word
FROM lexikon
WHERE lset BETWEEN @Low AND @High
ORDER BY frequency DESC
LIMIT @N
Podejrzewam, że indeks pokrycia (lset, frequency, word)
byłby przydatny, ale uważam, że może nie działać dobrze, jeśli w zakresie jest zbyt wiele lset
wartości (@High, @Low)
.
Zwykły włączony indeks (frequency DESC)
może być czasem wystarczający, gdy wyszukiwanie za pomocą tego indeksu daje wczesne @N
wiersze pasujące do warunków zakresu.
Ale wydaje się, że wydajność zależy w dużej mierze od wartości parametrów.
Czy istnieje sposób, aby działał szybko, niezależnie od tego, czy zakres (@Low, @High)
jest szeroki czy wąski i niezależnie od tego, czy słowa najwyższej częstotliwości znajdują się na szczęście w (wąskim) wybranym zakresie?
Czy pomógłby R-drzewo / indeks przestrzenny?
Dodanie indeksów, przepisanie zapytania, ponowne zaprojektowanie tabeli, nie ma ograniczeń.
źródło
lset,rset
iword
.Odpowiedzi:
Możesz być w stanie osiągnąć lepszą wydajność, wyszukując najpierw w rzędach o wyższych częstotliwościach. Można to osiągnąć poprzez „granulowanie” częstotliwości, a następnie przechodzenie przez nie proceduralnie, na przykład w następujący sposób:
- dane testowane i
lexikon
pozorowane:granule
analiza (głównie dla informacji i strojenia):najpierw funkcja skanowania wysokich częstotliwości:
wyniki (czasy należy prawdopodobnie wziąć ze szczyptą soli, ale każde zapytanie jest uruchamiane dwa razy, aby przeciwdziałać buforowaniu)
najpierw używając napisanej przez nas funkcji:
a następnie za pomocą prostego skanu indeksu:
W zależności od rzeczywistych danych prawdopodobnie będziesz chciał zmienić liczbę granulek i funkcję używaną do umieszczania w nich wierszy. Kluczowy jest tutaj rzeczywisty rozkład częstotliwości, podobnie jak oczekiwane wartości
limit
klauzuli i wielkościlset
poszukiwanych zakresów.źródło
width_granule=8
pomiędzygranulae_start
igranulae_end
poprzedniego poziomu?frequency
generowania: duża przerwa między 1e6 / 2 a 1e6 / 3, im wyższa liczba wierszy, tym mniejsza przerwa. W każdym razie dziękuję za to niesamowite podejście !!Ustawiać
Buduję na @ konfiguracji Jacka , aby ułatwić ludziom śledzić i porównywać. Testowane z PostgreSQL 9.1.4 .
Odtąd wybieram inną trasę:
Stolik pomocniczy
To rozwiązanie nie dodaje kolumn do oryginalnej tabeli, potrzebuje jedynie niewielkiej tabeli pomocniczej. Umieściłem go w schemacie
public
, użyj dowolnego wybranego schematu.Tabela wygląda następująco:
Ponieważ kolumna
cond
będzie dalej używana w dynamicznym SQL, musisz zabezpieczyć tę tabelę . Zawsze kwalifikuj się do schematu tabeli, jeśli nie masz pewności co do odpowiedniego prądusearch_path
i cofnij uprawnienia do zapisu zpublic
(i jakiejkolwiek innej niezaufanej roli):Tabela
lex_freq
służy trzem celom:Indeksy
Ta
DO
instrukcja tworzy wszystkie potrzebne indeksy:Wszystkie te częściowe indeksy razem obejmują tabelę jeden raz. Są mniej więcej tego samego rozmiaru co jeden podstawowy indeks na całej tabeli:
Dotychczas tylko 21 MB indeksów dla tabeli 50 MB.
Tworzę większość indeksów częściowych
(lset, frequency DESC)
. Druga kolumna pomaga tylko w szczególnych przypadkach. Ponieważ obie zaangażowane kolumny są tego samego typuinteger
, ze względu na specyfikę wyrównywania danych w połączeniu z MAXALIGN w PostgreSQL, druga kolumna nie powiększa indeksu. To niewielka wygrana za niewielką opłatą.Nie ma sensu tego robić w przypadku indeksów częściowych, które obejmują tylko jedną częstotliwość. Te są po prostu włączone
(lset)
. Utworzone indeksy wyglądają następująco:Funkcjonować
Funkcja jest nieco podobna stylem do rozwiązania @ Jacka:
Kluczowe różnice:
dynamiczny SQL z
RETURN QUERY EXECUTE
.Gdy wykonujemy kolejne kroki, beneficjentem może być inny plan zapytań. Plan zapytań dla statycznego SQL jest generowany raz, a następnie ponownie wykorzystywany - co może zaoszczędzić trochę narzutu. Ale w tym przypadku zapytanie jest proste, a wartości są bardzo różne. Dynamiczny SQL będzie wielką wygraną.
Dynamiczny
LIMIT
dla każdego kroku zapytania.Pomaga to na wiele sposobów: Po pierwsze, wiersze są pobierane tylko w razie potrzeby. W połączeniu z dynamicznym SQL może to na początku generować różne plany zapytań. Po drugie: nie ma potrzeby wprowadzania dodatkowego
LIMIT
wywołania funkcji w celu przycięcia nadwyżki.Reper
Ustawiać
Wybrałem cztery przykłady i każdy z nich przeprowadziłem trzy różne testy. Wziąłem najlepsze z pięciu, aby porównać z ciepłą pamięcią podręczną:
Surowe zapytanie SQL formularza:
To samo po utworzeniu tego indeksu
Potrzebuje mniej więcej tej samej przestrzeni, co wszystkie moje częściowe indeksy razem:
Funkcja
Wyniki
1: Całkowity czas działania: 315,458 ms
2: Całkowity czas działania: 36,458 ms
3: Całkowity czas działania: 0,330 ms
1: Całkowity czas działania: 294,819 ms
2: Całkowity czas działania: 18,915 ms
3: Całkowity czas działania: 1,414 ms
1: Całkowity czas działania: 426,831 ms
2: Całkowity czas działania: 217,874 ms
3: Całkowity czas działania: 1,611 ms
1: Całkowity czas działania: 2458.205 ms
2: Całkowity czas działania: 2458.205 ms - dla dużych zakresów lset skanowanie seq jest szybsze niż indeks.
3: Całkowity czas działania: 0,266 ms
Wniosek
Zgodnie z oczekiwaniami, korzyści płynące z funkcji rosną wraz z większymi zakresami
lset
i mniejszymiLIMIT
.Przy bardzo małych zakresach
lset
zapytanie surowe w połączeniu z indeksem jest w rzeczywistości szybsze . Będziesz chciał przetestować, a może rozgałęzić: surowe zapytanie dla małych zakresówlset
, w przeciwnym razie wywołanie funkcji. Możesz nawet po prostu wbudować to w funkcję „najlepszego z obu światów” - tak bym zrobił.W zależności od dystrybucji danych i typowych zapytań więcej kroków
lex_freq
może poprawić wydajność. Przetestuj, aby znaleźć najsłodsze miejsce. Dzięki narzędziom przedstawionym tutaj testowanie powinno być łatwe.źródło
Nie widzę żadnego powodu, aby kolumna słów znajdowała się w indeksie. Więc ten indeks
sprawi, że twoje zapytanie będzie działać szybko.
UPD
Obecnie nie ma możliwości utworzenia indeksu obejmującego w PostgreSQL. Dyskusje na temat tej funkcji można znaleźć na liście mailingowej PostgreSQL http://archives.postgresql.org/pgsql-performance/2012-06/msg00114.php
źródło
Korzystanie z indeksu GIST
To zależy od tego, co masz na myśli, kiedy pościsz: oczywiście musisz odwiedzić każdy wiersz w zakresie, ponieważ twoje zapytanie jest
ORDER freq DESC
. Nieśmiałe, że planista zapytań już to obejmuje, jeśli rozumiem pytanie,Tutaj tworzymy tabelę z 10 tys. Rzędów
(5::int,random()::double precision)
Indeksujemy to,
Pytamy o to,
Dostajemy
Seq Scan on t
. Jest tak po prostu dlatego, że nasze szacunki selektywności pozwalają pg stwierdzić, że dostęp do sterty jest szybszy niż skanowanie indeksu i ponowne sprawdzanie. Sprawiamy, że jest bardziej soczysty, wstawiając kolejne 1 000 000 wierszy(42::int,random()::double precision)
, które nie pasują do naszego „zakresu”.A następnie wymagamy
Tutaj możesz zobaczyć, że wykonujemy w 4.6 MS ze skanem tylko indeksu ,
Poszerzenie zakresu o całą tabelę, logicznie generuje kolejny skan sekwencyjny, a powiększenie go o kolejny miliard wierszy spowoduje kolejne skanowanie indeksu.
Podsumowując
źródło