Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Zachęcam do zapoznania się z wykładem z początku 2016 roku Andrieja Sałnikowa "Typowe błędy w aplikacjach prowadzące do bloat w postgresql"

W tym wykładzie przedstawię główne błędy w aplikacjach, które pojawiają się na etapie projektowania i pisania kodu aplikacji. Skupię się tylko na tych błędach, które prowadzą do bloat w Postgresql. Zazwyczaj jest to początek końca wydajności twojego systemu jako całości, mimo że początkowo nie widać żadnych przesłanek.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Witam wszystkich! Ten wykład nie jest tak techniczny jak poprzedni prezentowany przez mojego kolegę. Jest on głównie skierowany do programistów systemów backendowych, ponieważ mamy dość dużą liczbę klientów. I wszyscy popełniają te same błędy. O nich wam opowiem. Wyjaśnię, do jakich fatalnych i negatywnych skutków prowadzą te błędy.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Dlaczego te błędy są popełniane? Są one wynikiem dwóch czynników: po pierwsze, na zasadzie 'może się uda' oraz z braku wiedzy o mechanizmach zachodzących na poziomie między bazą a aplikacją, jak również w samej bazie danych.

Podam wam trzy przykłady z przerażającymi obrazkami ilustrującymi, jak wszystko się pogorszyło. Krótko opiszę mechanizmy, które tam zachodzą. A także jak sobie z nimi radzić, gdy już się pojawią i jakie metody prewencyjne stosować, by zapobiegać błędom. Opowiem o narzędziach pomocniczych i podam przydatne linki.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Użyłem testowej bazy danych, w której miałem dwie tabele. Jedna tabela z rachunkami klientów, a druga z operacjami na tych rachunkach. Z jakąś regularnością aktualizujemy salda na tych rachunkach.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Dane źródłowe tabeli: jest dość mała, 2 MB. Czas odpowiedzi dla bazy i konkretnej tabeli jest również bardzo dobry. A obciążenie wynosi – 2000 operacji na sekundę na tabeli.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

W trakcie tego wykładu będę wam pokazywał wykresy, aby było jasno widać, co się dzieje. Zawsze będą dwa slajdy z wykresami. Pierwszy slajd – to, co dzieje się ogólnie na serwerze.

W tej sytuacji widzimy, że nasza tabela ma rzeczywiście niewielki rozmiar. Indeks jest mały, wynoszący 2 MB. To pierwszy wykres po lewej stronie.

Średni czas odpowiedzi na serwerze również jest stabilny, niski. To wykres w prawym górnym rogu.

Lewy dolny wykres przedstawia najdłuższe transakcje. Widzimy, że transakcje są szybko realizowane. A autovakuum jeszcze nie działa, ponieważ był to test początkowy. W przyszłości będzie działał i przyniesie nam korzyści.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Drugi slajd zawsze będzie poświęcony testowanej tabeli. W tej sytuacji stale aktualizujemy stany na kontach klienta. I widzimy, że średni czas odpowiedzi dla operacji aktualizacji jest całkiem dobry, poniżej jednej milisekundy. Widzimy również, że zasoby procesora (to prawy górny wykres) są wykorzystywane równomiernie i pozostają na dość niskim poziomie.

Prawy dolny wykres pokazuje, ile pamięci operacyjnej i dyskowej przeszukujemy w poszukiwaniu potrzebnego wiersza przed jego aktualizacją. Liczba operacji na tabeli wynosi 2000 na sekundę, tak jak wcześniej mówiłem.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

A teraz następuje tragedia. Z jakiegoś powodu pojawia się długa zapomniana transakcja. Przyczyny są zazwyczaj banalne:

  • Jedna z najczęstszych to to, że w kodzie aplikacji zaczęliśmy odwoływać się do zewnętrznej usługi. A ta usługa nie odpowiada. Tzn. otworzyliśmy transakcję, wprowadziliśmy zmiany w bazie i poszliśmy w aplikacji poczytać pocztę lub skorzystać z innej usługi w naszej infrastrukturze, a ona z jakiegoś powodu nie odpowiada. I mamy zawieszoną sesję w stanie – nie wiadomo, kiedy to się rozwiąże.
  • Druga sytuacja, kiedy w kodzie z jakiegoś powodu wystąpił wyjątek. I w tym wyjątku nie obsłużyliśmy zamknięcia transakcji. W rezultacie uzyskaliśmy wiszącą sesję z otwartą transakcją.
  • I ostatni – to dość częsty przypadek. To niskiej jakości kod. Niektóre frameworki otwierają transakcję. Ona wisi, a wy możecie nie wiedzieć w aplikacji, że wisi.

Dokąd prowadzą takie rzeczy?

Prowadzą do tego, że nasze tabele i indeksy zaczynają się nagle znacznie powiększać. To właśnie ten efekt bloat. Dla bazy przejawi się to w tym, że znacznie wzrośnie czas odpowiedzi bazy danych, zwiększy się obciążenie serwera bazy danych. A w rezultacie ucierpi aplikacja. Ponieważ jeśli w kodzie spędzałeś 10 milisekund na zapytanie do bazy, 10 milisekund na swoją logikę, to twoja funkcja trwała 20 milisekund. A teraz sytuacja będzie znacznie gorsza.

Spójrzmy, co się dzieje. Lewy dolny wykres pokazuje, że mamy długą transakcję. A jeśli spojrzymy na lewy górny wykres, widzimy, że rozmiar tabeli z dwóch megabajtów nagle wzrósł do 300 megabajtów. Jednocześnie ilość danych w tabeli się nie zmieniła, więc jest tam sporo niepotrzebnych danych.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Ogólna sytuacja dotycząca średniego czasu odpowiedzi serwera również zmieniła się o kilka rzędów. To znaczy, że wszystkie zapytania serwera zaczęły znacznie spowalniać. A w międzyczasie uruchomiły się wewnętrzne procesy Postgresa w postaci autovacuum, które coś próbują robić i zużywają zasoby.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Co się dzieje z naszą tabelą? Podobnie. Średni czas odpowiedzi tabeli wzrósł o kilka rzędów. Jeśli chodzi o zużycie zasobów, to widzimy, że obciążenie procesora znacznie wzrosło. To przedstawia prawy górny wykres. Wzrosło, ponieważ procesor musi przeszukiwać mnóstwo nieprzydatnych wierszy w poszukiwaniu jednego potrzebnego. To przedstawia prawy dolny wykres. W rezultacie liczba wywołań na sekundę zaczęła znacznie maleć, ponieważ baza danych nie nadążała przetwarzać tego samego poziomu zapytań.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Musimy wrócić do normalności. Przeglądamy internet i dowiadujemy się, że długie transakcje prowadzą do problemów. Znajdujemy i kończymy tę transakcję. I wszystko wraca do normy. Wszystko działa jak należy.

Uspokajamy się, ale po pewnym czasie zaczynamy dostrzegać, że aplikacja działa nie tak, jak przed awarią. Zapytania wciąż są przetwarzane wolniej, a zwłaszcza wolniej. O półtorej do dwóch razy wolniej, konkretne w moim przykładzie. Obciążenie serwera również jest wyższe niż przed awarią.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Pytanie: 'Co się dzieje z bazą danych w tym momencie?'. A z bazą dzieje się następująca sytuacja. Na wykresie transakcji widać, że się zatrzymała i rzeczywiście nie ma długich transakcji. Ale rozmiar tabeli podczas awarii dramatycznie wzrósł. I od tego czasu się nie zmniejszył. Średni czas odpowiedzi w bazie ustabilizował się. Odpowiedzi wydają się być adekwatne przy akceptowalnej dla nas prędkości. Autovacuum stało się bardziej aktywne i zaczęło coś robić z tabelą, ponieważ musi przetworzyć większą ilość danych.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Specifically regarding the tested table with invoices, where we change the balances: the response time for the request seems to have returned to normal. But in reality, it is one and a half times higher.

And regarding the CPU load, we see that the CPU load has not returned to the desired level since the failure. The reasons lie precisely in the lower right graph. It is clear that some amount of memory is being exhausted there. That is, in order to find the necessary row, we are consuming server resources from the database by sifting through useless data. The number of transactions per second has stabilized.

Overall good, but the situation is worse than it was. There is a clear degradation of the database as a consequence of our application interacting with this database.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

And to understand what is happening there, if you weren't at the previous report, here's a bit of theory. Theory about the internal process. Why is autovacuum necessary and what does it do?

Briefly for understanding. At a certain point in time, we have a table. The table contains rows. These rows can be active, alive, and currently needed by us. In the picture, they are marked in green. And there are dead rows that have already been processed, updated, and new entries have appeared for them. They are noted as no longer relevant to the database. But they remain in the table due to the specifics of Postgres.

Why is autovacuum needed? At some point, autovacuum comes in, queries the database, and asks: "Please give me the id of the oldest transaction that is currently open in the database." The database returns this id. Autovacuum, based on it, sifts through the rows in the table. If it sees that some rows have been modified by much older transactions, it has the right to mark them as rows that we can reuse in the future by writing new data into them. This is a background process.

Meanwhile, we continue to work with the database, continuing to make some changes to the table. And for those rows that we can reuse, we write new data. And thus, we have a cycle, i.e., there are always some dead old rows appearing there, and instead of them, we write new rows that we need. And this is a normal state for working with PostgreSQL.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Co się wydarzyło podczas awarii? Jak przebiegał ten proces?

Mieliśmy tabelę w różnym stanie, z niektórymi aktywnymi, a niektórymi nieżyjącymi wierszami. Przyszedł autowakuum. Zapytał bazę danych, jaka jest nasza najstarsza transakcja i jaki ma identyfikator. Otrzymał ten identyfikator, który może mieć kilka godzin lub dziesięć minut. To zależy od tego, jak duże obciążenie masz w bazie danych. I poszedł szukać wierszy, które może oznaczyć jako do ponownego użycia. I nie znalazł takich wierszy w naszej tabeli.

Ale w międzyczasie wciąż pracujemy z tabelą. Coś w niej robimy, aktualizujemy, zmieniamy dane. A co powinna robić baza danych w tym czasie? Nie ma nic innego, jak tylko dopisywać nowe wiersze na końcu istniejącej tabeli. W ten sposób rozmiar tabeli zaczyna rosnąć.

Realnie potrzebujemy zielonych wierszy do pracy. Ale w trakcie takiego problemu okazuje się, że procent zielonych wierszy jest bardzo niski w całej objętości tabeli.

A kiedy wykonujemy zapytanie, baza danych musi przeszukać wszystkie wiersze: i czerwone, i zielone, aby znaleźć potrzebny wiersz. Efekt nadmiaru danych w tabeli nazywa się 'bloat', który dodatkowo zjada nasze miejsce na dysku. Pamiętacie, było 2 MB, stało się 300 MB? A teraz zamieńcie megabajty na gigabajty i wkrótce pozbędziecie się wszystkich zasobów dyskowych.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Jakie mogą być konsekwencje dla nas?

  • W moim przykładzie tabela i indeks zwiększyły się 150 razy. U niektórych naszych klientów zdarzały się bardziej fatalne przypadki, gdy po prostu zaczynało brakować miejsca na dysku.
  • Rozmiar tabel nigdy sam w sobie się nie zmniejszy. Autowakuum w niektórych przypadkach może odciąć koniec tabeli, jeśli znajdują się tam tylko martwe wiersze. Ale ponieważ następuje ciągła rotacja, jeden zielony wiersz może zawisnąć na końcu i nie być aktualizowany, podczas gdy wszystkie inne będą zapisywane gdzieś na początku tabeli. Ale to tak mało prawdopodobne zdarzenie, że nie ma co liczyć, że samodzielnie tabela zmniejszy swoje rozmiary.
  • Baza danych musi przeszukiwać całą stertę bezsensownych wierszy. I marnujemy zasoby dyskowe, zasoby procesora i energię elektryczną.
  • I to bezpośrednio wpływa na naszą aplikację, ponieważ jeśli na początku spędzaliśmy 10 milisekund na zapytanie i 10 milisekund na nasz kod, to podczas awarii zaczęliśmy spędzać sekundę na zapytaniu i 10 milisekund na kodzie, tzn. wydajność aplikacji spadła o rząd wielkości. A kiedy awaria została rozwiązana, zaczęliśmy poświęcać 20 milisekund na zapytanie i 10 milisekund na kod. Oznacza to, że i tak zanotowaliśmy półtorarazowy spadek wydajności. A wszystko to z powodu jednej transakcji, która się zawiesiła, przy czym być może z naszej winy.
  • I pytanie: „Jak wszystko przywrócić do normy?”, aby ponownie uzyskać dobrą wydajność i aby zapytania działały tak szybko, jak przed awarią.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

W tym celu istnieje określony cykl prac, które należy przeprowadzić.

Najpierw musimy znaleźć problematyczne tabele, które się rozrosły. Rozumiemy, że w przypadku niektórych tabel zapisy są bardziej aktywne, a w przypadku innych mniej aktywne. W tym celu używa się rozszerzenia pgstattuple. Po zainstalowaniu tego rozszerzenia możesz napisać zapytania, które pomogą ci znaleźć tabele, które rozrosły się na tyle, że są problematyczne.

Po znalezieniu tych tabel należy je skompresować. W tym celu dostępne są już narzędzia. W naszej firmie używamy trzech narzędzi. Pierwsze to wbudowane VACUUM FULL. Jest brutalne, surowe i bezwzględne, ale czasami jest bardzo przydatne. Pg_repack i pgcompacttable to zewnętrzne narzędzia do kompresji tabel. I są one bardziej delikatne dla bazy danych.

Są używane w zależności od tego, co jest dla ciebie wygodniejsze. Ale o tym opowiem na samym końcu. Najważniejsze to, że są trzy narzędzia. Jest z czego wybierać.

Po tym, jak wszystko naprawiliśmy i upewniliśmy się, że wszystko działa poprawnie, musimy wiedzieć, jak zapobiec tej sytuacji w przyszłości:

  • Zapobiega się temu dość łatwo. Należy monitorować czas trwania sesji na serwerze głównym. Szczególnie niebezpieczne są sesje w stanie idle in transaction. To te, które otworzyły transakcję, coś zrobiły i odeszły lub po prostu się zawiesiły, zniknęły w kodzie.
  • Dla was, jako dla programistów, ważne jest testowanie kodu w momencie powstawania tych sytuacji. To nie jest trudne do zrobienia. Będzie to przydatna kontrola. Unikniesz wielu „dziecinnych” problemów związanych z długotrwałymi transakcjami.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Na tych wykresach chciałem pokazać, jak zmieniła się tabela i zachowanie bazy danych po przeprowadzeniu VACUUM FULL na tabeli. Nie jest to produkcja.

Rozmiar tabeli natychmiast wrócił do normalnego stanu roboczego, kilku megabajtów. Na średni czas odpowiedzi serwera nie miało to większego wpływu.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Jednak w przypadku naszej testowanej tabeli, w której aktualizowaliśmy saldo na kontach, widzimy, że średni czas odpowiedzi na zapytanie o aktualizację danych w tabeli skrócił się do poziomu sprzed awarii. Zasoby wykorzystywane przez procesor do wykonania tego zapytania również spadły do poziomu sprzed awarii. A prawy dolny wykres pokazuje, że teraz natychmiast znajdujemy dokładnie ten wiersz, którego potrzebujemy, nie przeszukując zbioru martwych wierszy, które były przed kompresją tabeli. A średni czas zapytań pozostał na mniej więcej tym samym poziomie. Ale tutaj bardziej chodzi o błąd mojego sprzętu.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

To na razie koniec pierwszej opowieści. Jest ona najbardziej powszechna. I zdarza się każdemu, niezależnie od doświadczenia klienta, niezależnie od tego, jak wykwalifikowani są programiści. Prędzej czy później to się zdarza.

Druga historia, w której rozkładamy obciążenie i optymalizujemy zasoby serwera.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

  • Już urosłyśmy i staliśmy się poważnymi graczami. Rozumiemy, że mamy replikę i dobrze by było zbalansować obciążenie: pisać na Masterze, a czytać z repliki. Zwykle ta sytuacja występuje, gdy chcemy przygotować jakieś raporty lub ETL. I biznes bardzo się z tego cieszy. Bardzo pragnie różnorodnych raportów z masą skomplikowanej analityki.
  • Raporty są wielogodzinne, ponieważ skomplikowanej analityki nie obliczy się w milisekundach. My, jako dzielni chłopcy, piszemy kod. Robimy w aplikacji wstawki, że zapisujemy na Masterze, a raporty wykonujemy na replikach.
  • Rozkładamy obciążenie.
  • Wszystko działa świetnie. Jesteśmy świetni.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Jak wygląda ta sytuacja? Na tych wykresach dodałem także czas trwania transakcji z repliki. Wszystkie inne wykresy dotyczą tylko serwera Master.

Tabela raportów do tego momentu wzrosła. Jest ich więcej. Widzimy, że średni czas odpowiedzi serwera jest stabilny. Widzimy, że na replikacji mamy długą transakcję, która trwa 2 godziny. Zauważamy spokojną pracę autowakuum, które przetwarza martwe wiersze. Wszystko u nas w porządku.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Konkretnie w przypadku testowanej tabeli kontynuujemy aktualizację stanów na kontach. I również mamy stabilny czas odpowiedzi na zapytania, stabilne zużycie zasobów. Wszystko u nas w porządku.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Wszystko dobrze do momentu, kiedy nasze raporty zaczynają się wystrzeliwać z powodu konfliktu z replikacją. A wystrzeliwują się one z regularną częstotliwością.

Zagłębiamy się w internet i zaczynamy czytać, dlaczego tak się dzieje. I znajdujemy rozwiązanie.

Pierwsze rozwiązanie to zwiększenie opóźnienia replikacji. Wiemy, że nasz raport działa przez 3 godziny. Ustawiamy opóźnienie replikacji na 3 godziny. Uruchamiamy wszystko, ale nadal mamy problemy z tym, że raporty czasami wystrzeliwują się.

Chcemy, aby wszystko było idealne. Zagłębiamy się dalej. I znajdujemy w internecie świetną konfigurację – hot_standby_feedback. Włączamy ją. Hot_standby_feedback pozwala nam zatrzymać działanie autowakuum na Mistrzu. Dzięki temu całkowicie pozbywamy się konfliktów replikacji. I wszystko dobrze działa z raportami.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

A co się dzieje z Mistrzem w tym czasie? A z Mistrzem dzieje się totalna katastrofa. Obserwujemy teraz wykresy, kiedy włączyłem te dwa ustawienia. I widzimy, że sesja na replikacji w jakiś sposób zaczęła wpływać na sytuację na Mistrzu. Rzeczywiście wpływa, ponieważ wstrzymała autowakuum, które oczyszcza martwe wiersze. Rozmiar tabeli znowu skoczył w górę. Średni czas wykonania zapytań w całej bazie danych również skoczył w górę. Autowakuumy lekko się poddenerwowały.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Konkretnie w naszej tabeli widzimy, że aktualizacja danych również skoczyła w górę. Zużycie zasobów procesora również bardzo wzrosło. Znowu przeszukujemy dużą liczbę martwych, bezużytecznych wierszy. A czas odpowiedzi na tę tabelę, liczba transakcji spadła.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Jak to będzie wyglądać, jeśli nie wiemy, o czym mówiłem wcześniej?

  • Zaczynamy szukać problemów. Jeśli napotkaliśmy problemy w pierwszej części, wiemy, że może to być spowodowane długą transakcją i zagłębiamy się w Mistrza. Problem tkwi w Mistrzu. Zawiesza się. Grzeje się, jego Load Average jest blisko setki.
  • Zapytania tam się opóźniają, ale nie widzimy tam żadnych długotrwałych transakcji. I nie rozumiemy, o co chodzi. Nie wiemy, gdzie szukać.
  • Sprawdzamy sprzęt serwerowy. Może nasz RAID uległ awarii. Może mamy spaloną kość pamięci. Może być cokolwiek. Ale nie, serwery są nowe, wszystko działa doskonale.
  • Biegają wszyscy: administratorzy, programiści i dyrektor. Nic nie pomaga.
  • I w pewnym momencie wszystko nagle zaczyna się samo naprawiać.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Na replikacji w tym czasie zapytanie zostało wykonane i odeszło. Otrzymaliśmy raport. Biznes wciąż zadowolony. Jak widać, nasza tabela znowu urosła i nie zamierza się zmniejszać. Na wykresie sesji zostawiłem fragment tej długiej transakcji z replikacji, abyście mogli ocenić, jak długi czas mija, zanim sytuacja się stabilizuje.

Sesja odeszła. I dopiero po jakimś czasie serwer wraca do względnego porządku. A średni czas odpowiedzi na zapytania na serwerze Mistrza wraca do normy. Bo w końcu autovakuum otrzymał możliwość oczyszczenia, oznaczania tych martwych wierszy. I zaczął swoją pracę. A jak szybko to robi, tak szybko wrócimy do porządku.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

W tablicy testowej, gdzie aktualizujemy stany na kontach, widzimy dokładnie tę samą sytuację. Średni czas aktualizacji konta także stopniowo się normalizuje. Zasoby wykorzystywane przez procesor również maleją. A liczba transakcji na sekundę wraca do normy. Ale znowu nie do takiej normy, jaką mieliśmy przed awarią.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Tak czy inaczej, doświadczamy spadków wydajności, jak w pierwszym przypadku, od półtora do dwóch razy, a czasem nawet więcej.

Wygląda na to, że zrobiliśmy wszystko dobrze. Rozłożyliśmy obciążenie. Sprzęt nie stoi bezczynnie. Rozsądnie podzieliliśmy zapytania, ale mimo to wszystko poszło źle.

  • Czy nie włączać hot_standby_feedback? Tak, nie zaleca się jego włączania bez wyraźnych powodów. Ponieważ ta opcja wpływa bezpośrednio na serwer główny i wstrzymuje działanie autovacuum. Włączając to na jakiejś replikacji i zapominając o tym, możesz zniszczyć serwer główny i napotkać poważne problemy z aplikacją.
  • Czy zwiększyć max_standby_streaming_delay? Tak, jeżeli chodzi o raporty – to ma sens. Jeśli masz trzygodzinny raport i nie chcesz, aby się on zepsuł z powodu konfliktów replikacji, po prostu zwiększ opóźnienie. Długi raport nigdy nie wymaga danych, które trafiły do bazy chwilę temu. Jeżeli trwa on trzy godziny, oznacza to, że uruchamiasz go na podstawie danych sprzed jakiegoś czasu. I nie ma znaczenia, czy opóźnienie wynosi trzy godziny, czy sześć, ale będziesz otrzymywał stabilne raporty i unikniesz problemów z ich awarią.
  • Oczywiście, należy kontrolować długie sesje na replikach, szczególnie jeśli zdecydowałeś się włączyć hot_standby_feedback na replikach. Ponieważ może się zdarzyć wszystko. Przekazaliśmy tę replikę programiście, aby przetestował zapytania. Napisał on szalone zapytanie. Uruchomił je i poszedł napić się herbaty, a my otrzymujemy zablokowany serwer główny. Lub wpuściliśmy tam nieodpowiednią aplikację. Sytuacje bywają różne. Sesje na replikach należy kontrolować równie dokładnie, jak na serwerze głównym.
  • A jeśli masz szybkie i długie zapytania po replikach, to w tej sytuacji lepiej jest podzielić je dla rozłożenia obciążenia. To odniesienie do streaming_delay. Dla szybkich zapytań mieć jedną replikę z niewielkim opóźnieniem replikacji. Dla długich zapytań raportowych mieć replikę, która może mieć opóźnienie wynoszące 6 godzin, nawet do doby. To całkowicie normalna sytuacja.

Usuwamy konsekwencje w ten sam sposób:

  • Znajdujemy powiększone tabele.
  • I kompresujemy je najwygodniejszym narzędziem, które nam odpowiada.

Druga historia na tym się zakończyła. Przechodzimy do historii trzeciej.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Też dość zwyczajna dla nas, w której przeprowadzamy migrację.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

  • Każdy produkt oprogramowania się rozwija. Zmieniają się wymagania wobec niego. W każdym przypadku chcemy się rozwijać. I czasami musimy zaktualizować dane w tabeli, przeprowadzić aktualizację w ramach naszej migracji pod nową funkcjonalność, którą wprowadzamy w ramach naszego rozwoju.
  • Stary format danych nie jest wystarczający. Załóżmy, że teraz odwołamy się do drugiej tabeli, gdzie mam operacje na tych kontach. I załóżmy, że były w rublach, a postanowiliśmy zwiększyć precyzję i robić to w kopiejkach. Aby tego dokonać, musimy przeprowadzić aktualizację: pole z kwotą operacji pomnożyć przez sto.
  • W dzisiejszym świecie korzystamy z zautomatyzowanych narzędzi do kontroli wersji bazy danych. Załóżmy, Liquibase. Wpisujemy naszą migrację. Testujemy ją na naszej bazie testowej. Wszystko działa świetnie. Aktualizacja przebiega. Blokuje pracę na jakiś czas, ale zyskujemy zaktualizowane dane. Możemy uruchamiać nową funkcjonalność na tym. Wszystko przetestowane, sprawdzone. Wszystko potwierdzone.
  • Przeprowadziliśmy okresowe prace, przeprowadziliśmy migrację.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Oto migracja z aktualizacją przedstawiona przed tobą. Ponieważ są to operacje na kontach, tabela miała 15 GB. I ponieważ aktualizujemy każdy wiersz, w efekcie powiększyliśmy tabelę dwukrotnie, ponieważ zapisaliśmy każdy wiersz ponownie.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Podczas migracji nie mogliśmy nic robić z tą tabelą, ponieważ wszystkie zapytania do niej stanęły w kolejce i czekały, aż ta aktualizacja się zakończy. Ale tu chcę zwrócić uwagę na liczby, które zobaczysz na osi pionowej. T. j. mamy średni czas zapytania przed migracją w okolicach 5 milisekund i obciążenie procesora, liczba operacji blokowych po odczycie pamięci dysku jest mniejsza niż 7,5.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Przeprowadziliśmy migrację i znów napotkaliśmy problemy.

Migracja przebiegła pomyślnie, ale:

  • Stara funkcjonalność zaczęła działać wolniej.
  • Tabela znów zwiększyła swoje rozmiary.
  • Obciążenie serwera znów stało się wyższe, niż było.
  • I oczywiście, nadal zajmujemy się tą funkcjonalnością, która działała dobrze, nieco ją poprawiliśmy.

I to znów prowadzi do bloat, który ponownie utrudnia nam życie.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Tutaj demonstruję, że tabela, jak w dwóch poprzednich przypadkach, nie zamierza wracać do wcześniejszych rozmiarów. Średnie obciążenie serwera wydaje się być adekwatne.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

A jeśli spojrzymy na tabelę z rachunkami, zobaczymy, że średni czas zapytania wzrósł dwukrotnie w porównaniu do tej tabeli. Obciążenie procesora i liczba przetwarzanych w pamięci wierszy wzrosły powyżej 7,5, podczas gdy wcześniej było poniżej. W przypadku procesorów wzrosło dwukrotnie, w przypadku operacji blokowych wzrosło 1,5 razy, co oznacza, że doświadciliśmy degradacji wydajności serwera. A w konsekwencji – degradacji wydajności naszej aplikacji. Przy tym liczba wywołań pozostała na mniej więcej tym samym poziomie.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

I tutaj najważniejsze jest zrozumienie, jak właściwie przeprowadzać takie migracje. A są one konieczne. Dość regularnie przeprowadzamy te migracje.

  • Takie duże migracje nie są przeprowadzane automatycznie. Zawsze muszą być kontrolowane.
  • Wymagana jest kontrola ze strony znającej się osoby. Jeśli masz DBA w zespole, to niech to robi DBA. To jego praca. Jeśli nie, niech to robi najbardziej doświadczona osoba, która wie, jak pracować z bazami danych.
  • Nowy schemat bazy danych, nawet w przypadku, gdy aktualizujemy jedną kolumnę, zawsze przygotowujemy etapami, tj. wcześniej przed wdrożeniem nowej wersji aplikacji:
  • Dodawane są nowe pola, do których będziemy zapisywać właśnie zaktualizowane dane.
  • Przenosimy dane ze starego pola do nowego pola małymi partiami. Dlaczego to robimy? Po pierwsze, zawsze kontrolujemy przebieg tego procesu. Wiemy, ile już przenieśliśmy partii i ile nam pozostało.
  • A drugim pozytywnym efektem jest to, że między każdą taką partią zamykamy transakcję, otwieramy nową, co daje możliwość automatycznemu wakumowi działającemu na tabeli, oznaczeniu martwych wierszy do ponownego wykorzystania.
  • Dla wierszy, które będą się pojawiać w trakcie działania aplikacji (mamy jeszcze działającą starą aplikację) dodajemy wyzwalacz, który zapisuje nowe wartości w nowych polach. W naszym przypadku – jest to pomnożenie starej wartości przez sto.
  • Jeśli jesteśmy naprawdę upartymi i chcemy to samo pole, to po zakończeniu wszystkich migracji i przed wdrożeniem nowej wersji aplikacji, po prostu zmieniamy nazwy pól. Stare na jakieś wymyślone nazwy, a nowe pola zmieniamy w stare.
  • I dopiero po tym uruchamiamy nową wersję aplikacji.

I dzięki temu nie doświadczymy bloat i nie spadniemy na wydajności.

Na tym skończyła się trzecia historia.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat_approx.sql

A teraz trochę bardziej szczegółowo o narzędziach, które wspominałem w pierwszej historii.

Zanim zaczniesz szukać bloat, musisz koniecznie zainstalować rozszerzenie. pgstattuple.

Aby nie wymyślać zapytań, już przygotowaliśmy je w naszej pracy. Możesz je wykorzystać. Tutaj przedstawiono dwa zapytania.

  • Pierwsze działa dość długo, ale pokaże ci dokładne wartości bloat w tabeli.
  • Drugie działa szybciej i jest bardzo skuteczne, kiedy trzeba szybko ocenić – czy w tabeli jest bloat, czy nie. I powinieneś rozumieć, że bloat w tabeli Postgres zawsze istnieje. To cecha jego modelu MVCC.
  • A 20% bloat to w większości przypadków normalne dla tabel. Tzn. nie musisz się martwić i kompresować tej tabeli.

Jak zidentyfikować tabele, które się powiększyły, to zrozumieliśmy, zwłaszcza kiedy zwiększyły się o niepotrzebne dane.

Teraz o tym, jak naprawić bloat:

  • Jeśli mamy małą tabelę i dobre dyski, tzn. na tabeli do gigabajta można użyć VACUUM FULL. Weźmie on dla siebie wyłączną blokadę na tabelę na kilka sekund, ale za to szybko i skutecznie wszystko zrobi. Co robi VACUUM FULL? Bierze wyłączną blokadę na tabelę i przepisuje aktywne wiersze ze starych tabel do nowej tabeli. A na koniec zamienia je miejscami. Usuwa stare pliki, a nowe podstawia w miejsce starych. Ale w czasie swojej pracy bierze wyłączną blokadę tabeli. To oznacza, że nie możesz zrobić nic z tą tabelą: ani w niej pisać, ani jej czytać, ani modyfikować. I VACUUM FULL wymaga dodatkowego miejsca na dysku, aby zapisać dane.
  • Następne narzędzie pg_repack. Zasadniczo jest bardzo podobne do VACUUM FULL, ponieważ również przepisuje dane ze starych plików do nowych i zamienia je w tabeli. Ale na początku pracy nie bierze wyłącznej blokady tabeli, tylko w momencie, gdy ma już gotowe dane do zamiany plików. Wymagania dotyczące zasobów dyskowych są takie same jak u VACUUM FULL. Potrzebujesz dodatkowego miejsca na dysku, a to czasami bywa krytyczne, jeśli masz tabeli o terabajtowej wielkości. I jest dość zasobożerny, ponieważ prowadzi aktywną pracę z wejściem-wyjściem.
  • Trzecie narzędzie to pgcompacttableOna bardziej oszczędnie podchodzi do zasobów, ponieważ działa nieco na innych zasadach. Główna istota pgcompacttable polega na tym, że podczas aktualizacji w tabeli przenosi wszystkie aktywne wiersze na początek tabeli. Następnie uruchamia proces vacuum na tej tabeli, ponieważ wiemy, że na początku są aktywne, a na końcu martwe wiersze. A vacuum samo obcina ten ogon, tzn. dodatkowe miejsce na dysku nie jest w dużym stopniu wymagane. Przy tym można jeszcze ograniczyć jego zużycie zasobów.

Z narzędziami wszystko.

Typowe błędy w aplikacjach prowadzące do bloat w postgresql. Andriej Sałnikow

Jeśli temat bloat wyda ci się interesujący do dalszego zgłębiania, oto kilka przydatnych linków:

Starałem się tutaj bardziej pokazać straszak dla deweloperów, ponieważ są oni naszymi bezpośrednimi klientami baz danych i muszą rozumieć, do czego prowadzą różne działania. Mam nadzieję, że mi się to udało. Dziękuję za uwagę!

Pytania

Dziękuję za prezentację! Mówiłeś o tym, jak można identyfikować problemy. A jak można ich unikać? Tzn. miałem sytuację, kiedy zapytania zawieszały się nie tylko z powodu tego, że odwoływały się do jakiś zewnętrznych serwisów. To były po prostu jakieś straszne joins. Były jakieś małe, niegroźne zapytania, które przez dobę się wieszały, a potem zaczynały robić jakieś głupoty. Tzn. bardzo przypomina to, co opisujesz. Jak to monitorować? Siedzieć i ciągle patrzeć, które zapytanie utknęło? Jak można tego uniknąć?

W tym przypadku – to zadanie dla administratorów twojej firmy, a niekoniecznie dla DBA.

Jestem administratorem.

W PostgreSQL istnieje takie widok, jak pg_stat_activity, w którym pokazywane są wiszące zapytania. Możesz zobaczyć, jak długo tam wiszą.

Muszę co 5 minut wchodzić i sprawdzać?

Skonfiguruj cron i monitoruj. Jeśli masz długi zapytanie, napisz e-mail i tyle. Tzn. nie musisz patrzeć na to na bieżąco, można to zautomatyzować. Otrzymasz e-mail, na który reagujesz. Możesz też automatycznie reagować.

Czy są oczywiste powody, dlaczego to się dzieje?

Wymieniłem kilka. Inne są bardziej skomplikowanymi przykładami. Rozmowa o nich może trwać długo.

Dziękuję za prezentację! Chciałem zapytać o narzędzie pg_repack. Jeśli nie blokuje ono wyłącznie, to…

Ono blokuje wyłącznie.

to potencjalnie mogę stracić dane. Moja aplikacja nie powinna nic zapisywać w tym czasie?

Nie, działa spokojnie z tabelą, tzn. pg_repack najpierw przenosi wszystkie żywe wiersze, które są. Naturalnie w tabeli zachodzi jakaś operacja zapisu. On po prostu dokłada ten końcowy fragment.

Tzn. on w końcu jednak to robi?

Na końcu bierze wyłączną blokadę, aby zamienić te pliki miejscami.

Czy to będzie szybsze niż VACUUM FULL?

VACUUM FULL, kiedy się uruchomi, od razu bierze wyłączną blokadę. I dopóki wszystkiego nie skończy, nie zwolni blokady. A pg_repack bierze wyłączną blokadę tylko w momencie wymiany plików. W tym momencie nie zapiszesz nic, ale dane nie zostaną utracone, wszystko będzie w porządku.

Cześć! Opowiadałeś o pracy autovacuum. Był wykres z czerwonymi, żółtymi i zielonymi komórkami zapisu. Tzn. żółte – oznaczył jako usunięte. A w konsekwencji można w nich zapisać coś nowego?

Tak. Postgres nie usuwa wierszy. Ma taką specyfikę. Jeśli zaktualizujemy wiersz, stary jest oznaczany jako usunięty. Tam wstawia się id transakcji, która zmieniła ten wiersz, i zapisujemy nowy wiersz. I mamy sesje, które potencjalnie mogą je odczytać. W pewnym momencie stają się całkiem stare. Istota działania autovacuum polega na tym, że przeszukuje te wiersze i oznacza je jako niepotrzebne. I można tam zapisać nowe dane.

Zrozumiałem. Ale pytanie nieco nie dotyczy tego. Nie dokończyłem. Załóżmy, że mamy tabelę. Ma pola zmiennej długości. Jeśli spróbuję wstawić coś nowego, to może po prostu nie zmieścić się w starej komórce.

Nie, w każdym razie cała linia jest aktualizowana. W Postgres są dwa modele przechowywania danych. Wybiera je w zależności od typu danych. Są dane, które są przechowywane bezpośrednio w tabeli, a są też dane tos. To są duże objętości danych: tekst, json. Są one przechowywane w osobnych tabelach. I w przypadku tych tabel występuje ta sama historia z bloat, tzn. wszystko to samo. Po prostu są one wydzielone osobno.

Dziękuję za prezentację! Na ile akceptowalne jest używanie limitów czasowych dla zapytań statement timeout?

Bardzo akceptowalne. Używamy tego wszędzie. A ponieważ nie mamy własnych usług, świadczymy zdalne wsparcie, więc mamy do czynienia z różnorodnymi klientami. I wszyscy są z tego całkiem zadowoleni. Tzn. mamy zadania w cron, które to sprawdzają. Po prostu z klientem ustala się czas trwania sesji, przed końcem którego nie kończymy. Może to być minuta, może to być 10 minut. Zależy to od obciążenia bazy i jej celu. Ale wszyscy wykorzystujemy pg_stat_activity.

Dziękuję za prezentację! Staram się dostosować to, co mówisz, do moich aplikacji. I wydaje mi się, że wszędzie zaczynamy transakcję, wszędzie jawnie ją kończymy. Jeżeli wystąpi jakiś wyjątek, to i tak występuje rollback. I tutaj się zastanowiłem. Przecież transakcja może rozpocząć się niejawnie. To chyba sugestia dla dziewczyny. Jeżeli po prostu aktualizuję rekord, transakcja rozpocznie się w PostgreSQL i zakończy, gdy nastąpi rozłączenie?

Jeśli mówisz teraz o poziomie aplikacji, to zależy to od sterownika, który używasz, od ORM, który jest wykorzystywany. Jest tam bardzo wiele ustawień. Jeśli masz włączony auto commit on, to transakcja się rozpoczyna i zaraz zamyka.

Tzn. zamyka się ona zaraz po aktualizacji?

To zależy od ustawień. Jedno ustawienie, o którym wspomniałem, to auto commit on. Jest to dość powszechne. Jeśli jest włączone, to transakcja otwiera się i zamyka. Jeśli nie powiedziałeś jawnie „start transaction” i „end transaction”, tylko po prostu uruchomiłeś zapytanie w sesji.

Dzień dobry! Dziękuję za prezentację! Załóżmy, że mamy bazę, która rośnie rośnie, a na serwerze kończy się miejsce. Czy są jakieś narzędzia, które mogą rozwiązać tę sytuację?

Miejsce na serwerze naprawdę trzeba monitorować.

Na przykład DBA poszedł pić herbatę, był na wakacjach itd.

Kiedy tworzony jest system plików, minimalnie tworzone jest jakieś miejsce rezerwowe, do którego nie są zapisywane dane.

A jeśli całkowicie do zera?

To nazywa się przestrzenią zarezerwowaną, tj. można ją zwolnić, a w zależności od tego, jak dużą ją stworzono, otrzymujesz wolne miejsce. Domyślnie nie wiem, ile tego jest. A w innym przypadku – dostarczyć dyski, abyś miał miejsce na wykonanie operacji przywracania. Można usunąć jakąś tabelę, która na pewno nie jest potrzebna.

Nie ma innych narzędzi?

To zawsze jest praca ręczna. I na miejscu ustala się, co najlepiej zrobić, ponieważ są dane krytyczne i niekrytyczne. I dla każdej bazy i aplikacji, która z nią pracuje, zależy to od biznesu. Zawsze rozwiązuje się to na miejscu.

Dziękuję za wykład! Mam dwa pytania. Po pierwsze, pokazywałeś slajdy, na których widzieliśmy, że w przypadku zawieszonych transakcji rośnie zarówno objętość przestrzeni tabeli, jak i rozmiar indeksu. A co z indeksem?

One również pakują je.

Ale wakuum nie dotyczy indeksu?

Niektóre działają z indeksem. Na przykład pg_rapack, pgcompacttable. Wakuum rekreuje indeksy, dotyka ich. Istota VACUUM FULL polega na tym, aby wszystko przepisano, tj. działa ze wszystkimi.

I drugie pytanie. Nie zrozumiałem, dlaczego raporty na replikach tak bardzo zależą od samej replikacji. Wydawało mi się, że raporty to czytanie, a replikacja to zapis.

Gdzie występuje konflikt replikacji? Mamy Mastera, na którym przebiegają procesy. Mamy autowakuum. Co właściwie robi autowakuum? Usuwa jakieś stare wiersze. Jeśli w tym czasie na replikacji jest zapytanie, które odczytuje te stare wiersze, a na Masterze wystąpiła sytuacja, że autowakuum oznaczył te wiersze jako możliwe do przepisania, to je przepisujemy. I przychodzi pakiet danych, kiedy musimy przepisze wiersze, które są potrzebne zapytaniu na replikacji, proces replikacji poczeka na ten ustawiony przez ciebie timeout. A potem PostgreSQL zdecyduje, co jest dla niego ważniejsze. A replikacja jest dla niego ważniejsza niż zapytanie i odrzuci zapytanie, aby wykonać te zmiany na replikacji.

Andrzej, mam pytanie. Czy te niesamowite wykresy, które pokazywałeś podczas prezentacji, to efekt pracy jakiejś Twojej aplikacji? Jak stworzyłeś te wykresy?

To jest serwis Okmeter.

Czy to jest produkt komercyjny?

Tak. To jest produkt komercyjny.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster