Jak zmusić Postgres do korzystania z określonego indeksu?

112

Jak zmusić Postgresa do korzystania z indeksu, jeśli w przeciwnym razie nalegałby na skanowanie sekwencyjne?

mikrofon
źródło
Zduplikowane, patrz stackoverflow.com/questions/14554302/ ...
Grigorij Kislin
1
+1 Bardzo chciałbym zobaczyć tę funkcję. Nie chodzi o zwykłe wyłączenie skanowania sekwencyjnego, jak mówią inne odpowiedzi: potrzebujemy możliwości zmuszenia PG do korzystania z określonego indeksu . Dzieje się tak, ponieważ w rzeczywistości statystyki mogą być całkowicie błędne iw tym momencie musisz zastosować niewiarygodne / częściowe obejścia. Zgadzam się, że w prostych przypadkach należy najpierw sprawdzić indeksy i inne ustawienia, ale potrzebujemy tego do niezawodności i zaawansowanych zastosowań na dużych zbiorach danych.
collimarco
MySQL i Oracle mają to ... Nie jestem pewien, dlaczego planner Postgres jest tak zawodny.
Kevin Parker

Odpowiedzi:

103

Zakładając, że pytasz o typową funkcję „podpowiedzi do indeksu”, którą można znaleźć w wielu bazach danych, PostgreSQL nie zapewnia takiej funkcji. To była świadoma decyzja podjęta przez zespół PostgreSQL. Dobry przegląd tego, dlaczego i co można zamiast tego zrobić, można znaleźć tutaj . Powody są po prostu takie, że jest to hack wydajnościowy, który zwykle powoduje więcej problemów później, gdy zmieniają się dane, podczas gdy optymalizator PostgreSQL może ponownie ocenić plan na podstawie statystyk. Innymi słowy, to, co dziś może być dobrym planem zapytań, prawdopodobnie nie będzie dobrym planem zapytań na zawsze, a wskazówki dotyczące indeksu wymuszają określony plan zapytań na zawsze.

Jako bardzo tępy młotek, przydatny do testowania, możesz użyć parametrów enable_seqscani enable_indexscan. Widzieć:

Są to nie nadaje się do ciągłego użytku produkcyjnego . Jeśli masz problemy z wyborem planu zapytań, powinieneś zapoznać się z dokumentacją dotyczącą śledzenia problemów z wydajnością zapytań . Nie ustawiaj parametrów enable_i nie odchodź.

Jeśli nie masz bardzo dobrego powodu do korzystania z indeksu, Postgres może dokonywać właściwego wyboru. Czemu?

  • W przypadku małych tabel szybsze jest skanowanie sekwencyjne.
  • Postgres nie używa indeksów, gdy typy danych nie pasują do siebie, może być konieczne dołączenie odpowiednich rzutów.
  • Twoje ustawienia planera mogą powodować problemy.

Zobacz także ten stary post na grupie dyskusyjnej .

Patryk Kordylewski
źródło
4
Zgoda, zmuszanie postgres do robienia tego po swojemu zwykle oznacza, że ​​zrobiłeś to źle. 9/10 razy planista pokona wszystko, co wpadniesz na pomysł. Drugi raz to dlatego, że zrobiłeś to źle.
Kent Fredric
Myślę, że dobrym pomysłem jest sprawdzenie klas operatorów w posiadanym indeksie.
metdos
2
Nienawidzę powracać do starego pytania, ale często widzę w dokumentacji Postgresa, dyskusjach i tutaj, ale czy istnieje ogólna koncepcja tego, co kwalifikuje się do małego stołu ? Czy jest to coś w rodzaju 5000 rzędów, czy 50000 itd.?
waffl
1
@waffl Czy rozważałeś analizę porównawczą? Utwórz prostą tabelę z indeksem i towarzyszącą mu funkcją, aby wypełnić ją n wierszami losowych śmieci. Następnie zacznij przeglądać plan kwerend dla różnych wartości n . Kiedy zobaczysz, że zaczyna używać indeksu, powinieneś mieć odpowiedź. Możesz również uzyskać skanowanie sekwencyjne, jeśli PostgreSQL ustali (na podstawie statystyk), że skanowanie indeksu nie wyeliminuje również bardzo wielu wierszy. Dlatego benchmarking jest zawsze dobrym pomysłem, gdy masz prawdziwe obawy dotyczące wydajności. Z drugiej strony, anegdotyczne przypuszczenie, powiedziałbym, że kilka tysięcy to zazwyczaj „małe”.
jpmc26
11
Mając ponad 30 lat doświadczenia na platformach takich jak Oracle, Teradata i MSSQL, uważam, że optymalizator PostgreSQL 10 nie jest szczególnie inteligentny. Nawet z aktualnymi statystykami generuje mniej wydajne plany wykonania niż wymuszane w specjalnym kierunku. Dostarczenie wskazówek strukturalnych w celu skompensowania tych problemów zapewniłoby rozwiązanie umożliwiające PostgreSQL rozwój w większej liczbie segmentów rynku. MOIM ZDANIEM.
Guido Leenders,
75

Prawdopodobnie jedyny ważny powód do używania

set enable_seqscan=false

jest wtedy, gdy piszesz zapytania i chcesz szybko sprawdzić, jaki byłby plan zapytań, gdyby w tabelach były duże ilości danych. Lub oczywiście, jeśli chcesz szybko potwierdzić, że Twoje zapytanie nie korzysta z indeksu tylko dlatego, że zbiór danych jest zbyt mały.

Niraj Bhawnani
źródło
41
ta krótka odpowiedź faktycznie daje dobrą wskazówkę do celów testowych
dwery
3
Nikt nie odpowiada na pytanie!
Ivailo Bardarov
@IvailoBardarov Powodem, dla którego wszystkie te sugestie są tutaj, jest to, że PostgreSQL nie ma tej funkcji; była to świadoma decyzja podjęta przez programistów w oparciu o sposób jej użytkowania i długotrwałe problemy, jakie powoduje.
jpmc26
Niezła sztuczka do przetestowania: biegnij set enable_seqscan=false , uruchom zapytanie, a następnie szybko uruchom, set enable_seqscan=trueaby przywrócić poprawne zachowanie postgresql (i oczywiście nie rób tego na produkcji, tylko w fazie rozwoju!)
Brian Hellekin
2
@BrianHellekin Lepiej, SET SESSION enable_seqscan=falseaby wpływać tylko na siebie
Izkata
20

Czasami PostgreSQL nie dokonuje najlepszego wyboru indeksów dla określonego warunku. Na przykład załóżmy, że istnieje tabela transakcji z kilkoma milionami wierszy, których jest kilkaset na dany dzień, a tabela ma cztery indeksy: identyfikator_transakcji, identyfikator_klienta, datę i opis. Chcesz uruchomić następujące zapytanie:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description = 'Refund'
GROUP BY client_id

PostgreSQL może zdecydować się na użycie indeksu transaction_description_idx zamiast transaction_date_idx, co może spowodować, że zapytanie zajmie kilka minut zamiast mniej niż jednej sekundy. W takim przypadku możesz wymusić korzystanie z indeksu w dniu, modyfikując warunek w następujący sposób:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description||'' = 'Refund'
GROUP BY client_id
Ziggy Crueltyfree Zeitgeister
źródło
3
Dobry pomysł. Jednak gdy wyłączymy bieżące użycie indeksu tą metodą - optymalizator zapytań postgresql powraca do następnego odpowiedniego indeksu. Dlatego nie ma gwarancji, że optymalizator wybierze your_wanted_index, może być tak, że silnik postgresql po prostu wykona zamiast tego skanowanie sekwencji / klucza podstawowego. Wniosek - nie ma w 100% niezawodnej metody na wymuszenie użycia indeksu na serwerze PostgreSql.
Agnius Vasiliauskas
A co, jeśli nie ma wherewarunku, ale są dwie tabele lub połączone, a Postgres nie może pobrać indeksu.
Luna Lovegood
@Surya powyższe dotyczy zarówno GDZIE, jak i DOŁĄCZ ... NA warunkach
Ziggy Crueltyfree Zeitgeister
18

Krótka odpowiedź

Ten problem zwykle występuje, gdy szacowany koszt skanowania indeksu jest zbyt wysoki i nie odzwierciedla prawidłowo rzeczywistości. Może być konieczne obniżenierandom_page_cost parametru konfiguracyjnego, aby to naprawić. Z dokumentacji Postgres :

Zmniejszenie tej wartości [...] spowoduje, że system będzie preferował skanowanie indeksów; podniesienie go spowoduje, że skanowanie indeksów będzie wyglądało na relatywnie droższe.

Możesz sprawdzić, czy niższa wartość faktycznie spowoduje, że Postgres użyje indeksu (ale używaj go tylko do testowania ):

EXPLAIN <query>;              # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>;              # May use index scan now

Możesz przywrócić wartość domyślną SET random_page_cost = DEFAULT;ponownie za pomocą .

tło

Skanowanie indeksu wymaga niesekwencyjnego pobierania stron z dysku. Postgres używa random_page_costdo oszacowania kosztu takich niesekwencyjnych pobrań w stosunku do kolejnych pobrań. Wartość domyślna to 4.0, zakładając zatem średnią współczynnik kosztu wynosi 4 w porównaniu do pobierania sekwencyjnego (biorąc pod uwagę efekty buforowania).

Problem polega jednak na tym, że ta wartość domyślna jest nieodpowiednia w następujących ważnych scenariuszach z życia wziętych:

1) Dyski półprzewodnikowe

Jak przyznaje dokumentacja:

Pamięć masowa, która ma niski koszt losowego odczytu w porównaniu z sekwencyjnym, np. Dyski półprzewodnikowe, może być lepiej modelowana z niższą wartością for random_page_cost.

Zgodnie z ostatnim punktem tego slajdu z przemówienia na PostgresConf 2018, random_page_costpowinno być ustawione na coś pomiędzy 1.0a2.0 dla dysków półprzewodnikowych.

2) Dane w pamięci podręcznej

Jeśli wymagane dane indeksu są już buforowane w pamięci RAM, skanowanie indeksu będzie zawsze znacznie szybsze niż skanowanie sekwencyjne. Dokumentacja mówi:

W związku z tym, jeśli Twoje dane są prawdopodobnie całkowicie w pamięci podręcznej, [...] zmniejszenie random_page_costmoże być odpowiednie.

Problem polega na tym, że oczywiście nie możesz łatwo dowiedzieć się, czy odpowiednie dane są już w pamięci podręcznej. Jeśli jednak często zadawane są zapytania o określony indeks, a system ma wystarczającą ilość pamięci RAM, dane prawdopodobnie zostaną zapisane w pamięci podręcznej i random_page_costpowinny mieć niższą wartość. Będziesz musiał eksperymentować z różnymi wartościami i zobaczyć, co działa w Twoim przypadku.

Możesz także chcieć użyć rozszerzenia pg_prewarm do jawnego buforowania danych.


emkey08
źródło
2
Musiałem nawet ustawić random_page_cost = 0.1, aby skanowanie indeksu działało na dużej (~ 600M tabeli wierszy) w Pg 10.1 na Ubuntu. Bez poprawek skanowanie sekwencyjne (pomimo równoległości) trwało 12 minut (zwróć uwagę, że przeprowadzono analizę tabeli!). Dysk to SSD. Po poprawieniu czas wykonania wyniósł 1 sekundę.
Anatolij Aleksiejew
Uratowałeś mi dzień. Szalałem, próbując dowiedzieć się, jak dokładnie to samo zapytanie w tej samej bazie danych zajęło 30 sekund na jednym komputerze i mniej niż 1 na innym, nawet po uruchomieniu analizy na obu końcach ... Kogo to może dotyczyć: polecenie ' ALTER SYSTEM SET random_page_cost = x 'ustawia nową wartość domyślną globalnie.
Julien
10

Pytanie samo w sobie jest bardzo niepoprawne. Wymuszanie (na przykład przez wykonanie enable_seqscan = off) jest bardzo złym pomysłem. Warto sprawdzić, czy będzie szybszy, ale kod produkcyjny nigdy nie powinien wykorzystywać takich sztuczek.

Zamiast tego - wyjaśnij analizę swojego zapytania, przeczytaj je i dowiedz się, dlaczego PostgreSQL wybiera zły (Twoim zdaniem) plan.

Istnieją narzędzia w internecie, że pomoc przy czytaniu wyjaśnić analizować wyjście - jeden z nich jest explain.depesz.com - napisany przeze mnie.

Inną opcją jest dołączenie do kanału #postgresql w sieci irc freenode i rozmowa z gośćmi, którzy mogą Ci pomóc - ponieważ optymalizacja zapytania nie polega na „zadaniu pytania, uzyskaj odpowiedź, bądź szczęśliwy”. bardziej przypomina rozmowę, w której trzeba sprawdzić wiele rzeczy, wiele się nauczyć.

user80168
źródło
2

Istnieje sztuczka polegająca OFFSET 0na tym, aby popchnąć postgres, aby preferował seqscan, dodając a w podzapytaniu

Jest to przydatne do optymalizacji żądań łączących duże / ogromne tabele, gdy potrzebujesz tylko n pierwszych / ostatnich elementów.

Powiedzmy, że szukasz pierwszych / ostatnich 20 elementów obejmujących wiele tabel zawierających 100 tys. (Lub więcej) wpisów, nie ma sensu budować / łączyć całego zapytania ze wszystkimi danymi, gdy to, czego szukasz, znajduje się w pierwszych 100 lub 1000 wpisy. Na przykład w tym scenariuszu okazuje się, że skanowanie sekwencyjne przebiega ponad 10 razy szybciej.

zobacz Jak mogę uniemożliwić Postgresowi wstawianie podzapytania?

Antony Gibbs
źródło
Niezła sztuczka. Chociaż dobry optymalizator powinien oczywiście zoptymalizować przesunięcie 0 :-)
Guido Leenders