Jak zmusić Postgresa do korzystania z indeksu, jeśli w przeciwnym razie nalegałby na skanowanie sekwencyjne?
Jak zmusić Postgresa do korzystania z indeksu, jeśli w przeciwnym razie nalegałby na skanowanie sekwencyjne?
Odpowiedzi:
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_seqscan
i 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?
Zobacz także ten stary post na grupie dyskusyjnej .
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.
set enable_seqscan=false
, uruchom zapytanie, a następnie szybko uruchom, set enable_seqscan=true
aby przywrócić poprawne zachowanie postgresql (i oczywiście nie rób tego na produkcji, tylko w fazie rozwoju!)
SET SESSION enable_seqscan=false
aby wpływać tylko na siebie
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
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.
where
warunku, ale są dwie tabele lub połączone, a Postgres nie może pobrać indeksu.
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ą .
Skanowanie indeksu wymaga niesekwencyjnego pobierania stron z dysku. Postgres używa random_page_cost
do 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_cost
powinno być ustawione na coś pomiędzy 1.0
a2.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_cost
moż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_cost
powinny 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.
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ć.
Istnieje sztuczka polegająca OFFSET 0
na 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?