Ok, to całkiem klasyczny przypadek.
Za każdym razem, gdy używasz LIMIT
(lub podobnych, takich jak FETCH FIRST ... ROWS ONLY
), optymalizator próbuje zoptymalizować zapytanie, tak aby pobieranie tylko pierwszych wierszy było jak najszybsze. Oznacza to, że optymalizator preferuje plany wykonania, w których pierwsza wartość kosztu jest niska, a nie druga, pokazana w planie wykonania. Pamiętaj: dwie wartości kosztów wykazanych przez PostgreSQL (np cost=48.150..6,416.240
są koszty setup (48,150), a całkowity koszt realizacji (6,416.240)
„Problem” jest to, że masz indeks, który wspiera swoją klauzulę ORDER BY
.. Tak więc, PostgreSQL myśli, że może po prostu przejść przez ten indeks (w odwrotnej kolejności ze względu na modyfikator DESC
w zapytaniu) i sprawdzić dla każdego wiersza w drugiej tabeli, czy spełnia on drugą klauzulę WHERE
. Problem polega na tym, że optymalizator nie ma możliwości dowiedzenia się, czy będzie to jeden z pierwszych wierszy, czy raczej jeden na końcu (zgodnie z ORDER BY
). Optymalizator arbitralnie odgaduje, że pasujący wiersz będzie bardziej na początku niż na końcu.To optymistyczne oszacowanie jest następnie wykorzystywane do obliczenia wartości kosztowej, która okazuje się zbyt optymistyczna, aby PostgreSQL ostatecznie ustalił zły plan wykonania.
Po zmianie ORDER BY ... DESC
na ORDER BY ... ASC
optymalizator robi to samo arbitralne, ale optymistyczne oszacowanie, które w tym przypadku okazuje się być bardziej poprawne, dzięki czemu uzyskuje się lepszy czas wykonania.
Jednak z punktu widzenia optymalizacji podstawową przyczyną jest to, że optymalizator szacuje, że 2,491 wiersze będą zgodne z WHERE
klauzulą tango = 650727
. Gdy optymalizator prawidłowo oszacuje, że trafia on tylko w kilka wierszy, problem prawdopodobnie nie wystąpi.
Klauzula WHERE
jest na tyle banalna, że dobre oszacowanie nie powinno stanowić problemu. Główne pytanie brzmi: co z twoimi statystykami na tym stole?
Istnieje kilka sposobów, aby poradzić sobie z tym problemem:
- zaktualizować swoje statystyki (
ANALYZE
) i sprawdzić, czy to sam pomaga.
- Zwiększ liczbę najczęściej używanych wartości przechowywanych dla tej kolumny (
ALTER TABLE ... SET STATISTICS
). Zwiększa to również wielkość próby używanej do zbierania statystyk, co oznacza, że trwa to dłużej, ale daje dokładniejsze wyniki.
Teoretycznie powinno to wystarczyć, aby rozwiązać ten problem. Jednak inne opcje:
- Jeśli nie potrzebujemy indeks
created_at
z innych powodów (jak innych zapytań), pozbyć się go.
- Ponownie wpisz zapytanie, aby plan złego wykonania nie był już dostępny. W szczególności byłoby wspaniale, gdyby można było napisać zapytanie, aby klauzula
ORDER BY
używała tej samej tabeli co klauzula WHERE
: jeśli masz szczęście, możesz mieć kolumnę o numerze join_table
, która ma tę samą kolejność co , więc robi to nie ma żadnej różnicy, którą zamawiasz. Należy jednak zachować ostrożność, łatwo można się pomylić (np. Sekwencyjne liczby wypełnione sekwencjami mogą mieć kontroferty).
Wygląda na to, że możesz mieć indeks na kolumnie, którą zamawiasz, a więc kiedy twoje zamówienie jest zgodne z indeksem, jest szybkie, a gdy jest przeciwne do indeksu, musi je zamówić w pamięci, zanim będzie w stanie przedstawić wyniki. Czy możesz wymienić indeksy, które posiadasz na swoich stołach? –
Czy próbowałeś spojrzeć na inne problemy z wydajnością dotyczące zapytań z limitami dla postgresów na stackoverflow? Istnieje wiele tematów, może te pomoc. – Juru
Czy masz 3 indeksy lub 2 z jednym "indeksem złożonym", który zawiera "join_table.table_1_id", a także "join_table.table_2_id"? Z indeksem złożonym filtrowanie sprzężeń może być obsługiwane w całości przez ten indeks. Np .: tworzenie indeksu join_table_ix1 na join_table (table_2_id, table_1_id); –