6

Mam tabelę w postgresql, która zawiera tablicę, która jest aktualizowana stale.Optymalizacja kwerendy licznika dla PostgreSQL

W mojej aplikacji potrzebuję uzyskać liczbę wierszy, dla których określony parametr nie występuje w tej kolumnie tablicy. Moje zapytanie wygląda następująco:

select count(id) 
from table 
where not (ARRAY['parameter value'] <@ table.array_column) 

Ale kiedy wzrasta ilość rzędów i ilość egzekucji tego zapytania (kilka razy na sekundę, być może setki lub tysiące) wykonywanie decreses dużo, wydaje mi się, że liczenie w postgresql może mieć liniową kolejność wykonywania (nie jestem do końca tego pewien).

Zasadniczo moje pytanie brzmi:

Czy istniejący wzór nie jestem świadomy, że zastosowanie się do tej sytuacji? jakie byłoby najlepsze podejście do tego?

Wszelkie sugestie, które możesz mi podać, będą naprawdę mile widziane.

+0

Nie jestem pewien, ale myślę, że indeks GIN na table.array_column pomoże przyspieszyć ten proces. Aby dowiedzieć się, musisz uruchomić EXPLAIN. Zobacz tutaj: http://dba.stackexchange.com/a/27505/1822 –

+1

Ciężko będzie zrobić to wydajnie w postgresie, gdy stół stanie się duży. indeks ginu pomoże tylko podczas testowania "zawartego w", w przeciwieństwie do "nie zawartego" w twoim predykacie. Jeśli nie jest ważne, aby liczba była dokładna w 100%, możesz spróbować zapisać ją w warstwie aplikacji z pewnym TTL. Jeśli twoja szybkość zapisu w tabeli nie jest zbyt wysoka, możesz rozsądnie użyć wyzwalaczy do aktualizacji kolejnej tabeli zawierającej aktualne liczby. – dbenhur

+0

Najlepiej pokazać swoją wersję i 'wyjaśnić analizę'; zobacz http://stackoverflow.com/tags/postgresql-performance/info –

Odpowiedz

2

Czy istnieje istniejący schemat, o którym nie wiem, że ma zastosowanie do tej sytuacji ? jakie byłoby najlepsze podejście do tego?

Twoim najlepszym wyborem w tej sytuacji może być normalizacja twojego schematu. Podziel tablicę na stół. Dodaj indeks b-drzewa w tabeli właściwości lub zamów klucz podstawowy, aby był on skutecznie przeszukiwany przez property_id.

CREATE TABLE demo(id integer primary key); 
INSERT INTO demo (id) SELECT id FROM arrtable; 
CREATE TABLE properties (
    demo_id integer not null references demo(id), 
    property integer not null, 
    primary key (demo_id, property) 
); 
CREATE INDEX properties_property_idx ON properties(property); 

można następnie kwerendy właściwości:

SELECT count(id) 
FROM demo 
WHERE NOT EXISTS (
    SELECT 1 FROM properties WHERE demo.id = properties.demo_id AND property = 1 
) 

mogę oczekiwać, że jest to o wiele szybciej niż oryginalny zapytania, ale w rzeczywistości tak samo z tych samych danych próbki; działa w tym samym zakresie od 2s do 3s, jak w pierwotnym zapytaniu. Jest to ten sam problem, w którym wyszukiwanie tego, co jest , jest znacznie wolniejsze niż poszukiwanie tego, co jest tam; jeśli szukamy wierszy zawierających właściwość, możemy uniknąć seqscan z demo i po prostu skanować properties, aby bezpośrednio dopasować identyfikatory.

Znowu skanowanie seq w tabeli zawierającej tablicę wykonuje równie dobrze zadanie.

+0

dziękuję za to szczegółowe wyjaśnienie, tak właśnie w mojej obecnej sytuacji lepiej jest wykonać sekwencyjne liczenie lub pomyśleć o innym sposobie przechowywania informacji, aby wyszukiwanie było szybsze, znowu dzięki temu bardzo się przydało – jeruki

2

Myślę, że przy obecnym modelu danych masz pecha. Spróbuj wymyślić algorytm, który baza danych musi wykonać dla Twojego zapytania. Nie ma możliwości, aby działało bez sekwencyjnego skanowania danych.

Czy możesz uporządkować kolumnę tak, aby przechowywała odwrotność danych (tak aby zapytanie było select count(id) from table where ARRAY[‘parameter value’] <@ table.array_column)? Ta kwerenda używałaby indeksu gin/gist.

5

PostgreSQL faktycznie obsługuje indeksy GIN w kolumnach tablicowych. Niestety, nie wydaje się być użyteczny dla indeksów NOT ARRAY[...] <@ indexed_col, a indeksy GIN nie nadają się do często aktualizowanych tabel.

Demo:

CREATE TABLE arrtable (id integer primary key, array_column integer[]); 

INSERT INTO arrtable(1, ARRAY[1,2,3,4]); 

CREATE INDEX arrtable_arraycolumn_gin_arr_idx 
ON arrtable USING GIN(array_column); 

-- Use the following *only* for testing whether Pg can use an index 
-- Do not use it in production. 
SET enable_seqscan = off; 

explain (buffers, analyze) select count(id) 
from arrtable 
where not (ARRAY[1] <@ arrtable.array_column); 

Niestety, to pokazuje, że jak napisane, że nie można korzystać z indeksu. Jeśli nie negujesz warunku, możesz go użyć, aby wyszukać i policzyć wiersze, które zawierają element wyszukiwania (usuwając NOT).

Możesz użyć indeksu do zliczania wpisów, które do zawierają wartość docelową, a następnie odejmij ten wynik od liczby wszystkich wpisów. Ponieważ count, wszystkie wiersze w tabeli są dość powolne w PostgreSQL (9.1 i starsze) i wymaga skanowania sekwencyjnego, będzie to wolniejsze niż bieżące zapytanie. Jest możliwe, że na 9,2 skanowanie indeksu można używać tylko policzyć wiersze jeśli masz indeks B-drzewo na id, w tym przypadku może to być rzeczywiście OK:

SELECT (
    SELECT count(id) FROM arrtable 
) - (
    SELECT count(id) FROM arrtable 
    WHERE (ARRAY[1] <@ arrtable.array_column) 
); 

To gwarantowany wykonać gorzej niż Oryginalna wersja dla Pg 9.1 i poniżej, ponieważ oprócz seqscan twój oryginał wymaga tego również potrzebuje potrzebuje skanowania indeksu GIN. Przetestowałem to teraz w wersji 9.2 i wydaje się, że używa on indeksu do liczenia, więc warto go zbadać w wersji 9.2. Z jakiegoś mniej trywialny danych manekina:

drop index arrtable_arraycolumn_gin_arr_idx ; 
truncate table arrtable; 
insert into arrtable (id, array_column) 
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s; 
CREATE INDEX arrtable_arraycolumn_gin_arr_idx 
ON arrtable USING GIN(array_column); 

pamiętać, że indeks GIN jak to będzie spowolnić aktualizacje w dół dużo, i jest dość powolny, aby utworzyć w pierwszej kolejności. Nie nadaje się do tabel, które w ogóle są aktualizowane - na przykład Twój stół.

Gorsze, zapytanie za pomocą tego indeksu zajmuje do dwukrotności długość pierwotnego zapytania i co najwyżej połowę długości na tym samym zbiorze danych. To najgorsze w przypadku, gdy indeks nie jest bardzo selektywny, jak ARRAY[1] - 4s vs 2s dla pierwotnego zapytania. Tam, gdzie indeks jest wysoce selektywny (tzn. Nie ma wielu dopasowań, takich jak ARRAY[199]), trwa około 1,2 sekundy w porównaniu z oryginalnymi 3 znakami. Tego wskaźnika po prostu nie warto mieć dla tego zapytania.

Lekcja tutaj? Czasami właściwą odpowiedzią jest wykonanie sekwencyjnego skanowania.

Od tego nie zrobi dla swoich stóp hit, albo utrzymać zmaterializowanej widok z wyzwalaczem jak sugeruje @debenhur lub próbować odwrócić tablicę się lista parametrów, że pozycja nie nie mają więc może używać indeksu GiST, jak sugeruje @maniek.

Powiązane problemy