PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Optymalizacja zapytania licznika dla PostgreSQL

PostgreSQL faktycznie obsługuje indeksy GIN w kolumnach tablicy. Niestety, wydaje się, że nie nadaje się do użycia dla NOT ARRAY[...] <@ indexed_col i GIN indeksy i tak nie są odpowiednie dla 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 pokazuje to, że tak jak napisano, nie możemy używać indeksu. Jeśli nie negujesz warunku, możesz go użyć, dzięki czemu możesz wyszukiwać i liczyć wiersze, które wykonaj zawierać element wyszukiwania (poprzez usunięcie NOT ).

Możesz użyć indeksu do zliczania wpisów, które wykonaj zawierać wartość docelową, a następnie odjąć wynik od liczby wszystkich wpisów. Od count Tworzenie wszystkich wierszy w tabeli jest dość powolne w PostgreSQL (w wersji 9.1 i starszych) i wymaga skanowania sekwencyjnego, co w rzeczywistości będzie wolniejsze niż w przypadku bieżącego zapytania. Możliwe, że w wersji 9.2 skanowanie tylko indeksu może zostać użyte do zliczenia wierszy, jeśli masz indeks b-drzewa na id , w takim przypadku może to być w porządku:

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

Gwarantuje, że będzie działać gorzej niż oryginalna wersja dla strony 9.1 i niższych, ponieważ oprócz skanowania sekwencyjnego wymaga tego również oryginał również wymaga skanowania indeksu GIN. Testowałem to teraz na 9.2 i wydaje się, że używa indeksu do liczenia, więc warto go zbadać pod kątem 9.2. Z mniej trywialnymi fikcyjnymi danymi:

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

Zauważ, że indeks GIN taki jak ten spowalnia aktualizacje DUŻO i jest dość powolny w tworzeniu. Nie nadaje się do stołów, które są w ogóle aktualizowane - takich jak Twój stół.

Co gorsza, zapytanie korzystające z tego indeksu trwa do dwóch razy dłużej niż oryginalne zapytanie, a w najlepszym przypadku o połowę krócej na tym samym zestawie danych. Jest najgorszy w przypadkach, w których indeks nie jest zbyt selektywny, jak ARRAY[1] - 4s vs 2s dla oryginalnego zapytania. Gdzie indeks jest wysoce selektywny (tj:nie wiele dopasowań, jak ARRAY[199] ) działa w około 1,2 sekundy w porównaniu z 3 s oryginału. Ten indeks po prostu nie jest wart dla tego zapytania.

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

Ponieważ nie ma to wpływu na współczynniki trafień, utrzymuj zmaterializowany widok z wyzwalaczem, jak sugeruje @debenhur, albo spróbuj odwrócić tablicę tak, aby była listą parametrów, których wpis nie masz, więc możesz użyć indeksu GiST, jak sugeruje @maniek.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Debezium Heartbeat Action nie uruchamia się

  2. postgresql:błąd zduplikowanej wartości klucza narusza ograniczenie unikalności

  3. Postgres tworzący tabelę z tablicą kluczy obcych

  4. Konwertuj datę na dzień juliański w PostgreSQL

  5. Czy mogę automatycznie utworzyć tabelę w PostgreSQL z pliku csv z nagłówkami?