Kolejność kolumn w indeksie złożonym w PostgreSQL (i kolejność zapytań)


10

Mam stół z 50 000 rzędami. To właściwie tabela PostGIS.

Zapytanie składa się z 4 części (1 obowiązkowe) (3 opcjonalnie)

  1. pole przecięcia (prostokąt geograficzny) o długości 4 lat (używam st_intersects) [Obowiązkowe]
  2. Zakres dat (min., Maks.) W polu daty
  3. Typ pliku (zestaw do 8 wartości tekstowych) obecnie używa IN (.....), ale w razie potrzeby mogę utworzyć tabelę tymczasową. Widzę, że wiele osób nie lubi IN.
  4. Kraj (wartość tekstowa).

Oczekuję około 100 - 4000 zwróconych wierszy

Jeśli utworzę indeks złożony w tabeli, której kolumny powinienem użyć jako pierwszy. Drobnoziarnista to prawdopodobnie lokalizacja (dane są rozproszone po całym świecie). Obecnie mam go jako indeks GIST.

Pozostałe indeksy to BTREE.

Moja intuicja mówi, że używaj drobnoziarnistych i oczywiście na końcu. Np. Istnieje tylko około 12 typów plików, więc byłyby to bardzo duże segmenty dla indeksu.

Co mówią guru PostgreSQL i PostGIS (którzy znają wewnętrzne elementy systemu)?


AKTUALIZACJA:

Pozwól, że wyostrzę to pytanie.

  1. Nie chcę, żeby ktokolwiek musiał wykonywać pracę, którą powinienem wykonać. Za bardzo szanuję twój czas. Przejdę więc do wyjaśnienia przeanalizuj później.
  2. Szukałem jedynie wskazówek, wskazówek i wskazówek.
  3. Przeczytałem ten znakomity mały post: https://devcenter.heroku.com/articles/postgresql-indexes#managing-and-maintaining-indexes o indeksach
  4. Zwykle tworzę 4 osobne indeksy (geo-box, nazwa kraju, typ pliku i data), ale chcę zobaczyć, co zrobiłoby zapytanie złożone.

Powiedz mi, czy którekolwiek z tych założeń są błędne. (Jestem całkiem nowy w koncepcji indeksów złożonych)

  1. Porządek jest ważny. Wybierz jako pierwszy indeks ten, który najbardziej wycina wiersze (w moim przypadku najlepiej sprawdziłaby się lokalizacja (geografia), która jest prostym wielokątem lub wielokątem).
  2. Czasami zapytania pomijają indeksy. Ale jeśli utworzę zapytanie złożone z kluczem (# 1, # 2, # 3, # 4), to nawet jeśli użytkownik utworzy coś, co prosi o # 1, # 3, planista nadal będzie używał pojedynczego zapytania złożonego, ponieważ zamawia jest obsługiwany.
  3. Normalnie tworzyłbym trzy zapytania BTREE i jedno GIST (dla typu geograficznego). PostGIS nie obsługuje tworzenia związku z wielu typów indeksów. Będę musiał użyć GIST indeksu złożonego. Ale to nie powinno boleć.
  4. Jeśli utworzę dodatkowe indeksy złożone lub pojedyncze wartości, planista jest wystarczająco inteligentny, aby wybrać najbardziej inteligentny .....
  5. Nazwa kraju może mieć około 250 różnych wartości i jest oczywiście silnie powiązana z lokalizacją (geobox), ale jeśli kolejnym najlepszym indeksem do zmniejszenia rozmiaru wiersza jest typ_pliku, powinienem go użyć. Nie oczekuję, że użytkownicy często używają kraju lub daty w swoich zestawach zapytań.
  6. NIE muszę się martwić, że utworzenie indeksu złożonego z 4 kluczy znacznie zwiększy rozmiar danych indeksu. Tzn. Jeśli indeks jednego klucza wyniesie 90% wzrostu wydajności, nie zaszkodzi dodać 3 dodatkowe elementy, aby je zwiększyć. I odwrotnie, powinienem naprawdę utworzyć oba indeksy. Pojedynczy indeks geograficzny, a także indeks złożony i pozwól planistowi ustalić, który jest najlepszy, i weźmie pod uwagę rozmiar tabeli indeksów.

Ponownie, nie proszę nikogo o zaprojektowanie mojego rozwiązania, nie myślę o pracy innych. Ale potrzebuję rzeczy, których dokumentacja PostGreSQL nie mówi mi o implementacji

[Powodem, dla którego nie mam jeszcze wyniku WYJAŚNIENIA, jest to, że muszę utworzyć tę tabelę wierszy o wielkości 25 000 z tabeli wierszy o wielkości 24 mln. To zajmuje więcej czasu, niż myślałem. Grupuję rzeczy w 1000 grup przedmiotów i pozwalam użytkownikowi zapytać o tabelę wierszy o wielkości 25 KB. Ale moje następne pytanie będzie wymagało użycia wyników tego zapytania, aby przejść do tabeli wierszy MASTER 25M i wyciągnąć różne rzeczy, i to tam wydajność indeksu złożonego naprawdę HIT].


przykładowe zapytanie poniżej:


SELECT
    public.product_list_meta_mv.cntry_name       AS country,
    public.product_list_meta_mv.product_producer AS producer,
    public.product_list_meta_mv.product_name     AS prod_name,
    public.product_list_meta_mv.product_type     AS ptype,
    public.product_list_meta_mv.product_size     AS size,
    ST_AsGeoJSON(public.product_list_meta_mv.the_geom, 10, 2)          AS outline
FROM
    public.product_list_meta_mv 
WHERE
    public.product_list_meta_mv.cntry_name = 'Poland' 
AND
    ST_Intersects(public.product_list_meta_mv.the_geom,
    st_geogfromtext('SRID=4326;POLYGON((21.23107910156250 51.41601562500000,
                                        18.64379882812500 51.41601562500000,
                                        18.64379882812500 48.69415283203130,
                                        21.23107910156250 48.69415283203130,
                                        21.23107910156250 51.41601562500000))')) 
AND (date >= '1/2/1900 5:00:00 AM' 
 AND date <= '2/26/2014 10:26:44 PM')
AND (public.product_list_meta_mv.product_type in
    ('CIB10','DTED0','DTED1','DTED2','CIB01','CIB05')) ;

WYJAŚNIJ wyniki ANALIZY (nie wprowadziłem żadnych indeksów złożonych, a od prędkości, którą widzę, nie wiem, czy muszę).

"Bitmap Heap Scan on catalog_full cat  (cost=4.33..37.49 rows=1 width=7428) (actual time=1.147..38.051 rows=35 loops=1)"
"  Recheck Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"  Filter: (((type)::text = ANY ('{CADRG,CIB10,DTED1,DTED2}'::text[])) AND (_st_distance('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography, outline, 0::double precision, false) < 1e-005::double precision))"
"  Rows Removed by Filter: 61"
"  ->  Bitmap Index Scan on catalog_full_outline_idx  (cost=0.00..4.33 rows=8 width=0) (actual time=0.401..0.401 rows=96 loops=1)"
"        Index Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"Total runtime: 38.109 ms"

EXPLAIN ANALYZE SELECT pid,product_name,type,country,date,size,cocom,description,egpl_date,ST_AsGeoJSON(outline, 10, 2) AS outline 
FROM portal.catalog_full AS cat 
WHERE ST_Intersects(st_geogfromtext('SRID=4326;POLYGON((21.2200927734375 51.38031005859375, 18.65478515625 51.38031005859375, 18.65478515625 48.7298583984375, 21.2200927734375 48.7298583984375, 21.2200927734375 51.38031005859375))'), cat.outline) 
AND (cat.type in ('CADRG','CIB10','DTED1','DTED2'))

2
Proszę podać aktualne zapytanie.
ypercubeᵀᴹ

Czy „3 opcjonalne” oznacza, że ​​zapytanie może mieć 8 różnych odmian (w zależności od tego, czy opcje 2,3,4 są aktywowane, czy nie)?
ypercubeᵀᴹ

GDZIE są 4 ORAZ elementy. Na st_intersects jest wymagane, inne mogą tam być, lub mogą nie być. Ale chcę zająć się sprawą, w której wszyscy są obecni.

2
Głosowałem za przeniesieniem pytania do dba.se, jest to złożone zapytanie z wieloma warunkami zakresu.
ypercubeᵀᴹ

1
Pokaż EXPLAIN ANALYZEzapytanie.
Craig Ringer

Odpowiedzi:


4

W ramach mojej pracy utrzymuję dość dużą bazę danych PostgreSQL (około 120 GB na dysku, kilka tabel wielomilionowych) i zebrałem kilka sztuczek, jak przyspieszyć zapytania. Najpierw kilka uwag na temat twoich założeń:

  1. Tak, kolejność jest ważna, ale tylko ta pierwsza jest naprawdę inna, reszta to indeksy drugiej klasy.
  2. Nie jestem pewien, czy zawsze użyje obu, domyślam się, że planista zapytań użyje numeru 1, a następnie zrobi coś sprytnego z resztą.
  3. Nie mam doświadczenia z GIST.
  4. Tak, najpierw dodaj wszystkie indeksy, zobacz, co jest najczęściej używane i co zapewnia najlepszą wydajność.
  5. Sugerowałbym, abyś spróbował obu i zmierzył, co działa najlepiej. Spróbuj przepisać sql z różnymi podkwerendami, być może kraj i czas w jednym, a następnie dołącz zapytanie intersect. Nie zauważyłem żadnego problemu z wydajnością klauzul IN, o ile lista IN nie ma tysięcy elementów. Domyślam się, że kilka różnych zapytań specjalnie dostosowanych w zależności od dostępnych kryteriów wejściowych da najlepsze wyniki.
  6. Odradzałbym tworzenie 4-kierunkowego indeksu. Spróbuj utworzyć, a następnie sprawdź rozmiar, mogą stać się naprawdę ogromne. Z mojego doświadczenia wynika, że ​​cztery indeksy 1-klawiszowe były prawie tak szybkie jak pojedynczy indeks 4-kierunkowy. Sztuczka, która działa dobrze w przypadku niektórych konkretnych zapytań, to indeksy częściowe, tj. Coś takiego:

    UTWÓRZ INDEKS NA table_x (key1, key2, key3) GDZIE some_x_column = 'XXXX';

W pliku .psqlrc utworzyłem aliasy z zapytaniami, które pomagają znaleźć indeksy do dodania lub usunięcia. Zapraszam do ich obejrzenia na GitHub: .psql

Często używam: seq_scans i: bigtables, a następnie \ d nazwa_tabeli, aby uzyskać szczegółowe informacje na temat tabeli. Nie zapomnij zresetować statystyk po dokonaniu pewnych zmian, wybierz pg_stat_reset ();


1
To są doskonałe wskazówki. Skorzystałem z twojej rady, a następnie wykorzystałem to do przeprowadzenia eksperymentu na znacznie większym stole, który utrzymujemy (43 miliony wierszy). Wyniki są na: dba.stackexchange.com/questions/61084/…
Dr.YSG

1

Myślę, że najbardziej prawdopodobną pomocą (jeśli cokolwiek) byłoby dodanie produktu_typ jako drugiej kolumny do indeksu gist. Ale nie wiedząc, ile wierszy pasuje do każdego z warunków AND (osobno) dla typowych / problematycznych zapytań, możemy tylko zgadywać.

Kiedy do tego podchodzę, pierwszą rzeczą, którą robię, jest uruchomienie zapytania w uproszczonej formie, w którym klauzula WHERE ma tylko jeden warunek, każdy wzięty kolejno, w ramach WYJAŚNIJ ANALIZĘ. Spójrz zarówno na szacowane wiersze, jak i na rzeczywiste wiersze dla każdego z nich.


zobacz moją aktualizację powyżej, ale myślę, że dajesz mi dobrą przewagę, pomyśl o uporządkowaniu indeksów, o które najszybciej skraca wyjście wiersza. Czy to prawda?
Dr.YSG
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.