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

Gdy autovacuum nie odkurza

Kilka tygodni temu wyjaśniłem podstawy tuningu autovacuum. Na koniec tego posta obiecałem, że wkrótce zajmę się problemami z odkurzaniem. Cóż, zajęło to trochę dłużej niż planowałem, ale zaczynamy.

Aby szybko podsumować, autovacuum jest procesem w tle, który oczyszcza martwe rzędy, np. stare usunięte wersje wierszy. Możesz również wykonać czyszczenie ręcznie, uruchamiając VACUUM , ale autovacuum robi to automatycznie w zależności od ilości martwych wierszy w tabeli, we właściwym momencie – nie za często, ale wystarczająco często, aby kontrolować ilość „śmieci”.

Ogólnie rzecz biorąc, autovacuum nie może działać zbyt często – czyszczenie następuje dopiero po osiągnięciu pewnej liczby martwych wierszy zgromadzonych w tabeli. Jednak może to być opóźnione z różnych powodów, w wyniku czego tabele i indeksy stają się większe niż pożądane. I to jest dokładnie temat tego postu. Więc jacy są najczęstsi winowajcy i jak ich zidentyfikować?

Ograniczanie

Jak wyjaśniono w podstawach strojenia, autovacuum pracownicy są ograniczani do wykonywania tylko określonej ilości pracy w przedziale czasu. Domyślne limity są dość niskie – około 4 MB/s zapisów, 8 MB/s odczytów. Jest to odpowiednie dla małych maszyn, takich jak Raspberry Pi lub małe serwery sprzed 10 lat, ale obecne maszyny są znacznie potężniejsze (zarówno pod względem procesora, jak i we/wy) i obsługują znacznie więcej danych.

Wyobraź sobie, że masz kilka dużych stołów i kilka małych. Jeśli wszystkie trzy autovacuum pracownicy zaczynają sprzątać duże stoły, żaden z małych nie zostanie odkurzony, niezależnie od ilości martwych rzędów, które gromadzą. Zidentyfikowanie tego nie jest szczególnie trudne, zakładając, że masz wystarczający monitoring. Poszukaj okresów, w których wszystkie autovacuum pracownicy są zajęci, podczas gdy stoły nie są odkurzane pomimo zgromadzenia wielu martwych rzędów.

Wszystkie niezbędne informacje znajdują się w pg_stat_activity (liczba autovacuum procesy robocze) i pg_stat_all_tables (last_autovacuum i n_dead_tup ).

Zwiększanie liczby autovacuum pracowników nie jest rozwiązaniem, ponieważ całkowita ilość pracy pozostaje taka sama. Możesz określić limity ograniczania przepustowości na tabelę, wyłączając tego pracownika z całkowitego limitu, ale to nadal nie gwarantuje, że w razie potrzeby będą dostępni pracownicy.

Właściwym rozwiązaniem jest dostrojenie ograniczania przepustowości przy użyciu limitów rozsądnych w odniesieniu do konfiguracji sprzętowej i wzorców obciążenia. Niektóre podstawowe zalecenia dotyczące ograniczania przepustowości zostały wymienione w poprzednim poście. (Oczywiście, jeśli możesz zmniejszyć liczbę martwych wierszy generowanych w bazie danych, byłoby to idealne rozwiązanie.)

Od tego momentu będziemy zakładać, że dławienie nie jest problemem, tj. że autovacuum pracownicy nie są nasyceni przez długi czas, a czyszczenie jest uruchamiane na wszystkich stołach bez nieuzasadnionych opóźnień.

Długie transakcje

Tak więc, jeśli stół jest regularnie odkurzany, z pewnością nie może gromadzić wielu martwych rzędów, prawda? Niestety nie. Wiersze nie są faktycznie „usuwalne” natychmiast po usunięciu, ale tylko wtedy, gdy nie ma żadnych transakcji, które mogłyby je zobaczyć. Dokładne zachowanie zależy od tego, co robią (były) inne transakcje i od poziomu serializacji, ale ogólnie:

PRZECZYTAJ ZAANGAŻOWANO

  • uruchamianie zapytań blokuje czyszczenie
  • bezczynne transakcje blokują czyszczenie tylko wtedy, gdy wykonały zapis
  • bezczynne transakcje (bez żadnych zapisów) nie blokują czyszczenia (ale i tak nie jest dobrą praktyką trzymanie ich w pobliżu)

SERIALIZOWALNY

  • uruchamianie zapytań blokuje czyszczenie
  • bezczynne transakcje blokują czyszczenie (nawet jeśli tylko odczytywały)

W praktyce jest to oczywiście bardziej zniuansowane, ale wyjaśnienie wszystkich różnych bitów wymagałoby najpierw wyjaśnienia, jak działają XID i migawki, a to nie jest celem tego postu. To, co naprawdę powinieneś wziąć z tego, to to, że długie transakcje to zły pomysł, szczególnie jeśli te transakcje mogły spowodować zapisy.

Oczywiście istnieją całkowicie uzasadnione powody, dla których możesz potrzebować przechowywać transakcje przez długi czas (np. jeśli musisz zapewnić KWAS dla wszystkich zmian). Ale upewnij się, że nie dzieje się to niepotrzebnie, np. z powodu złego projektu aplikacji.

Nieco nieoczekiwaną konsekwencją tego jest wysokie zużycie procesora i we/wy, ze względu na autovacuum biegać w kółko, bez czyszczenia martwych rzędów (lub tylko kilku z nich). Z tego powodu stoły nadal kwalifikują się do czyszczenia w następnej rundzie, powodując więcej szkody niż pożytku.

Jak to wykryć? Po pierwsze, musisz monitorować długoterminowe transakcje, szczególnie te bezczynne. Wszystko, co musisz zrobić, to odczytać dane z pg_stat_activity . Definicja widoku zmienia się nieco w wersji PostgreSQL, więc być może trzeba będzie to trochę poprawić:

SELECT xact_start, state FROM pg_stat_activity;

-- count 'idle' transactions longer than 15 minutes (since BEGIN)
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - xact_start) > interval '15 minutes'

-- count transactions 'idle' for more than 5 minutes
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - state_change) > interval '5 minutes'

Możesz też po prostu użyć jakiejś istniejącej wtyczki monitorującej, np. check_postgres.pl. Obejmują one już ten rodzaj kontroli rozsądku. Musisz zdecydować, jaki jest rozsądny czas trwania transakcji/zapytania, który zależy od aplikacji.

Od PostgreSQL 9.6 możesz również użyć idle_in_transaction_session_timeout aby transakcje bezczynne przez zbyt długi czas były automatycznie kończone. Podobnie w przypadku długich zapytań istnieje statement_timeout .

Kolejną przydatną rzeczą jest VACUUM VERBOSE co faktycznie powie ci, ile martwych wierszy nie można jeszcze usunąć:

db=# VACUUM verbose z;
INFO:  vacuuming "public.z"
INFO:  "z": found 0 removable, 66797 nonremovable row versions in 443 out of 443 pages
DETAIL:  12308 dead row versions cannot be removed yet.
...

Nie powie ci, który backend uniemożliwia czyszczenie, ale jest to całkiem wyraźny znak tego, co się dzieje.

Uwaga: . Nie możesz łatwo uzyskać tych informacji z autovacuum ponieważ jest rejestrowany tylko za pomocą DEBUG2 domyślnie (i na pewno nie chcesz działać z tym poziomem dziennika w środowisku produkcyjnym).

Długie zapytania dotyczące gorącego czuwania

Załóżmy, że tabele są odkurzane w odpowiednim czasie, ale nie usuwają martwych krotek, co powoduje rozdęcie tabeli i indeksu. Monitorujesz pg_stat_activity i nie ma długotrwałych transakcji. Jaki może być problem?

Jeśli masz replikę strumieniową, prawdopodobnie występuje problem. Jeśli replika używa hot_standby_feedback=on , zapytania dotyczące repliki działają prawie jak transakcje na podstawowym, w tym blokowanie czyszczenia. Oczywiście hot_standby_feedback=on jest używany dokładnie podczas uruchamiania długich zapytań (np. obciążeń analitycznych i BI) na replikach, aby zapobiec anulowaniu z powodu konfliktów replikacji.

Niestety, będziesz musiał wybrać – albo pozostaw hot_standby_feedback=on i akceptuj opóźnienia w sprzątaniu lub zajmuj się anulowanymi zapytaniami. Możesz także użyć max_standby_streaming_delay aby ograniczyć wpływ, chociaż nie zapobiega to całkowicie anulowaniu (więc nadal musisz ponawiać zapytania).

Właściwie jest teraz trzecia opcja – replikacja logiczna. Zamiast korzystać z fizycznej replikacji strumieniowej dla repliki BI, możesz skopiować zmiany za pomocą nowej replikacji logicznej, dostępnej w PostgreSQL 10. Replikacja logiczna rozluźnia sprzężenie między pierwotną a repliką i sprawia, że ​​klastry są w większości niezależne (są czyszczone niezależnie, itp.).

Rozwiązuje to dwa problemy związane z fizyczną replikacją strumieniową — opóźnione czyszczenie podstawowych lub anulowanych zapytań w replice BI. W przypadku replik służących do celów DR replikacja strumieniowa pozostaje jednak właściwym wyborem. Ale te repliki nie są (lub nie powinny) uruchamiać długich zapytań.

Uwaga: Chociaż wspomniałem, że replikacja logiczna będzie dostępna w PostgreSQL 10, znaczna część infrastruktury była dostępna w poprzednich wydaniach (szczególnie w PostgreSQL 9.6). Więc możesz to zrobić nawet w starszych wersjach (zrobiliśmy to dla niektórych naszych klientów), ale PostgreSQL 10 sprawi, że będzie to znacznie wygodniejsze i wygodniejsze.

Problem z autoanalyze

Szczegółem, który możesz przegapić, jest to, że autovacuum pracownicy faktycznie wykonują dwa różne zadania. Po pierwsze czyszczenie (jak przy uruchamianiu VACUUM ), ale także zbieranie statystyk (jak przy uruchamianiu ANALYZE ). I oba części są dławione za pomocą autovacuum_cost_limit .

Ale jest duża różnica w obsłudze transakcji. Ilekroć VACUUM część osiąga autovacuum_cost_limit , pracownik zwalnia migawkę i przez chwilę śpi. ANALYZE jednak musi działać w jednej migawce/transakcji, co tak blokowanie czyszczenia.

To elegancki sposób na strzelenie sobie w stopę, szczególnie jeśli robisz też coś takiego:

  • zwiększ default_statistics_target budować dokładniejsze statystyki z większych próbek
  • niższy autovacuum_analyze_scale_factor aby częściej zbierać statystyki

Niezamierzoną konsekwencją jest oczywiście to, że ANALYZE będzie się zdarzać częściej, potrwa znacznie dłużej i będzie (w przeciwieństwie do VACUUM część) zapobiec czyszczeniu. Rozwiązanie jest zazwyczaj dość proste – nie obniżaj autovacuum_analyze_scale_factor zbyt wiele. Uruchamianie ANALYZE za każdym razem 10% zmian w tabeli powinno w większości przypadków wystarczyć.

n_dead_tup

Ostatnią rzeczą, o której chciałbym wspomnieć, są zmiany w pg_stat_all_tables.n_dead_tup wartości. Możesz pomyśleć, że wartość jest prostym licznikiem, zwiększanym za każdym razem, gdy tworzona jest nowa martwa krotka i zmniejszana za każdym razem, gdy jest czyszczona. Ale w rzeczywistości jest to tylko szacunkowa liczba martwych krotek, zaktualizowana przez ANALYZE . W przypadku małych tabel (mniej niż 240 MB) nie jest to duża różnica, ponieważ ANALYZE czyta całą tabelę, więc jest całkiem dokładny. W przypadku dużych tabel może się to jednak nieco zmienić w zależności od tego, jaki podzbiór tabeli jest próbkowany. I obniżenie autovacuum_vacuum_scale_factor sprawia, że ​​jest bardziej losowy.

Więc bądź ostrożny patrząc na n_dead_tup w systemie monitoringu. Nagłe spadki lub wzrosty wartości mogą być po prostu spowodowane ANALYZE ponowne obliczenie innego oszacowania, a nie z powodu rzeczywistego czyszczenia i/lub nowych martwych krotek pojawiających się w tabeli.

Podsumowanie

Podsumowując to w kilku prostych punktach:

  • autovacuum może działać tylko wtedy, gdy nie ma transakcji, które mogłyby potrzebować martwych krotek.
  • Długo działające zapytania blokują czyszczenie. Rozważ użycie statement_timeout aby ograniczyć szkody.
  • Długotrwała transakcja może blokować czyszczenie. Dokładne zachowanie zależy od takich rzeczy, jak poziom izolacji lub to, co wydarzyło się w transakcji. Monitoruj je i usuwaj je, jeśli to możliwe.
  • Długotrwałe zapytania dotyczące replik z hot_standby_feedback=on może również blokować czyszczenie.
  • autoanalyze jest również dławiony, ale w przeciwieństwie do VACUUM część przechowuje pojedynczy zrzut (a tym samym blokuje czyszczenie).
  • n_dead_tup to tylko oszacowanie obsługiwane przez ANALYZE , więc spodziewaj się pewnych wahań (szczególnie przy dużych stołach).

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Postgres Zmień liczbę całkowitą kolumny na Boolean

  2. Kolumna postgreSQL nie istnieje, gdzie foo jest wartością

  3. Heroku pg:pociągnięcie nie powiodło się wypełnienie schematu

  4. ActiveRecord::StatementInvalid:PG InFailedSqlTransaction

  5. Wybrać pierwszy wiersz w każdej grupie GROUP BY?