Wydajność to jedno z najważniejszych i najbardziej złożonych zadań podczas zarządzania bazą danych. Może mieć na to wpływ konfiguracja, sprzęt, a nawet konstrukcja systemu. Domyślnie PostgreSQL jest skonfigurowany z myślą o kompatybilności i stabilności, ponieważ wydajność zależy w dużej mierze od sprzętu i samego systemu. Możemy mieć system z dużą ilością odczytywanych danych, ale informacje nie zmieniają się często. Albo możemy mieć system, który pisze w sposób ciągły. Z tego powodu niemożliwe jest zdefiniowanie domyślnej konfiguracji, która działa dla wszystkich typów obciążeń.
Na tym blogu zobaczymy, w jaki sposób można analizować obciążenie lub zapytania, które są uruchomione. Następnie przejrzymy kilka podstawowych parametrów konfiguracyjnych, aby poprawić wydajność naszej bazy danych PostgreSQL. Jak wspomnieliśmy, zobaczymy tylko niektóre parametry. Lista parametrów PostgreSQL jest obszerna, dotknęlibyśmy tylko niektórych kluczowych. Jednak zawsze można zapoznać się z oficjalną dokumentacją, aby zagłębić się w parametry i konfiguracje, które wydają się najważniejsze lub przydatne w naszym środowisku.
WYJAŚNIJ
Jednym z pierwszych kroków, jakie możemy podjąć, aby zrozumieć, jak poprawić wydajność naszej bazy danych, jest analiza wykonanych zapytań.
PostgreSQL opracowuje plan zapytań dla każdego otrzymanego zapytania. Aby zobaczyć ten plan, użyjemy EXPLAIN.
Struktura planu zapytania to drzewo węzłów planu. Węzły na niższym poziomie drzewa to węzły skanowania. Zwracają surowe wiersze z tabeli. Istnieją różne typy węzłów skanowania dla różnych metod dostępu do tabeli. Wyjście EXPLAIN ma linię dla każdego węzła w drzewie planu.
world=# EXPLAIN SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
QUERY PLAN
--------------------------------------------------------------------------
Nested Loop (cost=0.00..734.81 rows=50662 width=144)
-> Seq Scan on city t1 (cost=0.00..93.19 rows=347 width=31)
Filter: ((id > 100) AND (population > 700000))
-> Materialize (cost=0.00..8.72 rows=146 width=113)
-> Seq Scan on country t2 (cost=0.00..7.99 rows=146 width=113)
Filter: (population < 7000000)
(6 rows)
To polecenie pokazuje, w jaki sposób zostaną przeskanowane tabele w naszym zapytaniu. Zobaczmy, jakie wartości odpowiadają tym wartościom, które możemy zaobserwować w naszym wyjaśnieniu.
- Pierwszy parametr pokazuje operację, którą silnik wykonuje na danych w tym kroku.
- Szacowany koszt uruchomienia. Jest to czas spędzony przed rozpoczęciem fazy wyjściowej.
- Szacowany koszt całkowity. Jest to określone przy założeniu, że węzeł planu został uruchomiony do końca. W praktyce węzeł nadrzędny węzła może przestać czytać wszystkie dostępne wiersze.
- Szacowana liczba wierszy wyprowadzanych przez ten węzeł planu. Ponownie zakłada się, że węzeł został uruchomiony do końca.
- Szacowana średnia szerokość wierszy wyjściowych przez ten węzeł planu.
Najbardziej krytyczną częścią wyświetlacza jest szacowany koszt wykonania wyciągu, który jest domysłem planisty, ile czasu zajmie wykonanie wyciągu. Porównując skuteczność jednego zapytania z drugim, w praktyce będziemy porównywać ich wartości kosztów.
Ważne jest, aby zrozumieć, że koszt węzła wyższego poziomu obejmuje koszt wszystkich jego węzłów podrzędnych. Ważne jest również, aby zdać sobie sprawę, że koszt odzwierciedla tylko rzeczy, na których zależy planistom. W szczególności koszt nie uwzględnia czasu spędzonego na przesyłaniu wierszy wyników do klienta, co może być ważnym czynnikiem w rzeczywistym upływającym czasie; ale planista ignoruje go, ponieważ nie może go zmienić, modyfikując plan.
Koszty mierzone są w dowolnych jednostkach określonych przez parametry kosztowe planisty. Tradycyjną praktyką jest mierzenie kosztów w jednostkach pobierania stron na dysku; oznacza to, że seq_page_cost jest konwencjonalnie ustawiane na 1.0, a inne parametry kosztów są ustawiane względem tego.
WYJAŚNIJ ANALIZĘ
Dzięki tej opcji EXPLAIN wykonuje zapytanie, a następnie wyświetla prawdziwe liczby wierszy i rzeczywisty czas działania skumulowany w każdym węźle planu, wraz z tymi samymi szacunkami, które pokazuje zwykły EXPLAIN.
Zobaczmy przykład użycia tego narzędzia.
world=# EXPLAIN ANALYZE SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.00..734.81 rows=50662 width=144) (actual time=0.081..22.066 rows=51100 loops=1)
-> Seq Scan on city t1 (cost=0.00..93.19 rows=347 width=31) (actual time=0.069..0.618 rows=350 loops=1)
Filter: ((id > 100) AND (population > 700000))
Rows Removed by Filter: 3729
-> Materialize (cost=0.00..8.72 rows=146 width=113) (actual time=0.000..0.011 rows=146 loops=350)
-> Seq Scan on country t2 (cost=0.00..7.99 rows=146 width=113) (actual time=0.007..0.058 rows=146 loops=1)
Filter: (population < 7000000)
Rows Removed by Filter: 93
Planning time: 0.136 ms
Execution time: 24.627 ms
(10 rows)
Jeśli nie znajdziemy powodu, dla którego nasze zapytania trwają dłużej niż powinny, możemy sprawdzić ten blog, aby uzyskać więcej informacji.
PRÓŻNIA
Proces VACUUM odpowiada za kilka zadań konserwacyjnych w obrębie bazy danych, z których jedno odzyskuje pamięć zajmowaną przez martwe krotki. W normalnym działaniu PostgreSQL krotki, które są usuwane lub przestarzałe przez aktualizację, nie są fizycznie usuwane ze swojej tabeli; pozostają obecne aż do wykonania PRÓŻNI. Dlatego konieczne jest przeprowadzanie PRÓŻNI okresowo, zwłaszcza w często aktualizowanych tabelach.
Jeśli ODKURZACZ zajmuje zbyt dużo czasu lub zasobów, oznacza to, że musimy to robić częściej, aby każda operacja miała mniej do czyszczenia.
W każdym razie może zajść potrzeba wyłączenia VACUUM, na przykład podczas ładowania danych w dużych ilościach.
ODKURZACZ po prostu odzyskuje przestrzeń i udostępnia ją do ponownego wykorzystania. Ta forma polecenia może działać równolegle z normalnym odczytywaniem i zapisywaniem tabeli, ponieważ nie uzyskuje się blokady na wyłączność. Jednak dodatkowa przestrzeń nie jest zwracana do systemu operacyjnego (w większości przypadków); jest dostępny tylko do ponownego wykorzystania w tej samej tabeli.
VACUUM FULL przepisuje całą zawartość tabeli w nowym pliku dyskowym bez dodatkowej przestrzeni, co pozwala na powrót niewykorzystanej przestrzeni do systemu operacyjnego. Ten formularz jest znacznie wolniejszy i wymaga wyłącznej blokady na każdym stole podczas przetwarzania.
ANALIZA PRÓŻNI wykonuje PRÓŻNIĘ, a następnie ANALIZĘ dla każdego wybranego stołu. Jest to praktyczny sposób łączenia rutynowych skryptów konserwacyjnych.
ANALYZE zbiera statystyki dotyczące zawartości tabel w bazie danych i przechowuje wyniki w pg_statistic. Następnie planista zapytań wykorzystuje te statystyki, aby pomóc określić najbardziej wydajne plany wykonania zapytań.
Pobierz oficjalny dokument już dziś Zarządzanie i automatyzacja PostgreSQL za pomocą ClusterControlDowiedz się, co musisz wiedzieć, aby wdrażać, monitorować, zarządzać i skalować PostgreSQLPobierz oficjalny dokumentParametry konfiguracyjne
Aby zmodyfikować te parametry musimy wyedytować plik $PGDATA/postgresql.conf. Musimy pamiętać, że niektóre z nich wymagają ponownego uruchomienia naszej bazy danych.
max_connections
Określa maksymalną liczbę jednoczesnych połączeń z naszą bazą danych. Istnieją zasoby pamięci, które można skonfigurować dla każdego klienta, dlatego maksymalna liczba klientów może sugerować maksymalną ilość używanej pamięci.
superuser_reserved_connections
W przypadku osiągnięcia limitu max_connection, połączenia te są zarezerwowane dla superużytkownika.
shared_buffers
Ustawia ilość pamięci używanej przez serwer bazy danych na bufory pamięci współużytkowanej. Jeśli masz dedykowany serwer bazy danych z 1 GB lub więcej pamięci RAM, rozsądna wartość początkowa dla shared_buffers to 25% pamięci systemu. Większe konfiguracje dla shared_buffers zazwyczaj wymagają odpowiedniego zwiększenia max_wal_size, aby wydłużyć proces zapisywania dużych ilości nowych lub zmodyfikowanych danych przez dłuższy czas.
temp_buffery
Ustawia maksymalną liczbę buforów tymczasowych używanych w każdej sesji. Są to lokalne bufory sesji używane tylko do dostępu do tabel tymczasowych. Sesja przypisze tymczasowe bufory zgodnie z potrzebami do limitu podanego przez temp_buffers.
praca_mem
Określa ilość pamięci, która będzie używana przez wewnętrzne operacje tablic ORDER BY, DISTINCT, JOIN i hash przed zapisaniem do plików tymczasowych na dysku. Podczas konfigurowania tej wartości musimy wziąć pod uwagę, że kilka sesji wykonuje te operacje w tym samym czasie, a każda operacja będzie mogła wykorzystać tyle pamięci, ile określono w tej wartości, zanim zacznie zapisywać dane w plikach tymczasowych.
Ta opcja nosiła nazwę sort_mem w starszych wersjach PostgreSQL.
maintenance_work_mem
Określa maksymalną ilość pamięci używanej przez operacje konserwacji, takie jak VACUUM, CREATE INDEX i ALTER TABLE ADD FOREIGN KEY. Ponieważ tylko jedna z tych operacji może być wykonana w tym samym czasie przez sesję, a instalacja zwykle nie ma wielu działających jednocześnie, może być większa niż work_mem. Większe konfiguracje mogą poprawić wydajność przywracania VACUUM i bazy danych.
Kiedy autovacuum jest wykonywane, ta pamięć może być przypisana, ile razy jest konfigurowany parametr autovacuum_max_workers, więc musimy wziąć to pod uwagę lub w inny sposób skonfigurować parametr autovacuum_work_mem, aby zarządzać tym osobno.
fsync
Jeśli fsync jest włączone, PostgreSQL spróbuje upewnić się, że aktualizacje są fizycznie zapisywane na dysku. Gwarantuje to, że klaster bazy danych może zostać przywrócony do spójnego stanu po awarii systemu operacyjnego lub sprzętu.
Chociaż wyłączenie fsync ogólnie poprawia wydajność, może spowodować utratę danych w przypadku awarii zasilania lub awarii systemu. Dlatego zaleca się wyłączenie fsync tylko wtedy, gdy możesz łatwo odtworzyć całą bazę danych z danych zewnętrznych.
segmenty_punktu kontrolnego (PostgreSQL <9.5)
Maksymalna liczba segmentów plików rekordów między automatycznymi punktami kontrolnymi WAL (każdy segment ma zwykle 16 megabajtów). Zwiększenie tego parametru może wydłużyć czas potrzebny na naprawę usterek. W systemie o dużym natężeniu ruchu może mieć wpływ na wydajność, jeśli jest ustawiony na bardzo niską wartość. Zaleca się zwiększenie wartości checkpoint_segments w systemach z wieloma modyfikacjami danych.
Dobrą praktyką jest również zapisywanie plików WAL na dysku innym niż PGDATA. Jest to przydatne zarówno do zrównoważenia zapisu, jak i bezpieczeństwa w przypadku awarii sprzętu.
Od PostgreSQL 9.5 zmienna konfiguracyjna "checkpoint_segments" została usunięta i została zastąpiona przez "max_wal_size" i "min_wal_size"
max_wal_size (PostgreSQL>=9,5)
Maksymalny rozmiar, jaki może rosnąć WAL między punktami kontrolnymi. W szczególnych okolicznościach rozmiar WAL może przekroczyć max_wal_size. Zwiększenie tego parametru może wydłużyć czas potrzebny na naprawę usterek.
min_wal_size (PostgreSQL>=9,5)
Gdy plik WAL jest przechowywany poniżej tej wartości, jest przetwarzany do wykorzystania w przyszłości w punkcie kontrolnym, a nie usuwany. Można to wykorzystać, aby zapewnić zarezerwowanie wystarczającej ilości miejsca WAL do obsługi skoków w użyciu WAL, na przykład podczas wykonywania dużych zadań wsadowych.
wal_sync_method
Metoda używana do wymuszenia aktualizacji WAL na dysku. Jeśli fsync jest wyłączone, to ustawienie nie ma wpływu.
wal_buffery
Ilość pamięci współdzielonej używanej dla danych WAL, które nie zostały jeszcze zapisane na dysku. Domyślne ustawienie to około 3% shared_buffers, nie mniej niż 64KB lub więcej niż rozmiar segmentu WAL (zwykle 16MB). Ustawienie tej wartości na co najmniej kilka MB może poprawić wydajność zapisu na serwerze z wieloma współbieżnymi transakcjami.
efektywny_rozmiar_cache
Ta wartość jest używana przez planistę zapytań do uwzględnienia planów, które mogą lub nie mieszczą się w pamięci. Jest to brane pod uwagę w szacunkach kosztów stosowania indeksu; wysoka wartość zwiększa prawdopodobieństwo użycia skanowania indeksu, a niska wartość zwiększa prawdopodobieństwo użycia skanowania sekwencyjnego. Rozsądna wartość to 50% pamięci RAM.
default_statistics_target
PostgreSQL zbiera statystyki z każdej tabeli w swojej bazie danych, aby zdecydować, w jaki sposób będą wykonywane na nich zapytania. Domyślnie nie zbiera zbyt wielu informacji, a jeśli nie otrzymujesz dobrych planów wykonania, powinieneś zwiększyć tę wartość, a następnie ponownie uruchomić ANALIZA w bazie danych (lub poczekać na AUTOVACUUM).
synchronous_commit
Określa, czy zatwierdzanie transakcji będzie czekać na zapisanie rekordów WAL na dysku, zanim polecenie zwróci klientowi wskazanie „sukcesu”. Możliwe wartości to:„on”, „remote_apply”, „remote_write”, „local” i „off”. Ustawienie domyślne to „włączone”. Gdy jest wyłączona, może wystąpić opóźnienie między powrotem klienta, a gwarantowanym zabezpieczeniem transakcji przed blokadą serwera. W przeciwieństwie do fsync, wyłączenie tego parametru nie stwarza żadnego ryzyka niespójności bazy danych:awaria systemu operacyjnego lub bazy danych może spowodować utratę niektórych rzekomo popełnionych ostatnio dokonanych transakcji, ale stan bazy danych będzie dokładnie taki sam, jak w przypadku tych transakcji została skasowana bez zarzutu. Dlatego dezaktywacja synchronous_commit może być użyteczną alternatywą, gdy wydajność jest ważniejsza niż dokładna pewność co do trwałości transakcji.
Logowanie
Istnieje kilka rodzajów danych do rejestrowania, które mogą być przydatne lub nie. Zobaczmy niektóre z nich:
- log_min_error_statement:Ustawia minimalny poziom rejestrowania.
- log_min_duration_statement:Służy do rejestrowania wolnych zapytań w systemie.
- log_line_prefix:Dołącza informacje na początku każdej linii dziennika.
- log_statement:Możesz wybrać pomiędzy NONE, DDL, MOD, ALL. Używanie „all” może powodować problemy z wydajnością.
Projekt
W wielu przypadkach projekt naszej bazy danych może mieć wpływ na wydajność. Musimy być ostrożni w naszym projektowaniu, normalizując nasz schemat i unikając zbędnych danych. W wielu przypadkach wygodnie jest mieć kilka małych stolików zamiast jednego wielkiego. Ale jak powiedzieliśmy wcześniej, wszystko zależy od naszego systemu i nie ma jednego możliwego rozwiązania.
Musimy również odpowiedzialnie korzystać z indeksów. Nie powinniśmy tworzyć indeksów dla każdego pola lub kombinacji pól, ponieważ chociaż nie musimy podróżować po całej tabeli, wykorzystujemy miejsce na dysku i dodajemy narzut na operacje zapisu.
Innym bardzo przydatnym narzędziem jest zarządzanie pulą połączeń. Jeśli mamy system z dużym obciążeniem, możemy to wykorzystać, aby uniknąć nasycania połączeń w bazie danych i móc je ponownie wykorzystać.
Sprzęt
Jak wspomnieliśmy na początku tego bloga, sprzęt jest jednym z ważnych czynników, które bezpośrednio wpływają na wydajność naszej bazy danych. Zobaczmy kilka punktów, o których należy pamiętać.
- Pamięć:Im więcej mamy pamięci RAM, tym więcej danych w pamięci możemy obsłużyć, a to oznacza lepszą wydajność. Szybkość zapisu i odczytu na dysku jest znacznie wolniejsza niż w pamięci, dlatego im więcej informacji możemy mieć w pamięci, tym lepszą będziemy mieli wydajność.
- CPU:Może to nie ma sensu, ale im więcej mamy procesora, tym lepiej. W każdym razie nie jest to najważniejsze pod względem sprzętu, ale jeśli możemy mieć dobry procesor, nasza zdolność przetwarzania ulegnie poprawie, co bezpośrednio wpływa na naszą bazę danych.
- Dysk twardy:Mamy kilka rodzajów dysków, których możemy używać, SCSI, SATA, SAS, IDE. Posiadamy również dyski półprzewodnikowe. Musimy porównać jakość / cenę, którą powinniśmy wykorzystać do porównania jego szybkości. Ale typ dysku nie jest jedyną rzeczą do rozważenia, musimy również zobaczyć, jak je skonfigurować. Jeśli zależy nam na dobrej wydajności, możemy użyć RAID10, zachowując warstwy WAL na innym dysku poza RAID. Nie zaleca się używania RAID5, ponieważ wydajność tego typu RAID dla baz danych nie jest dobra.
Wniosek
Po uwzględnieniu punktów wymienionych na tym blogu, możemy przeprowadzić test porównawczy, aby zweryfikować zachowanie bazy danych.
Ważne jest również, aby nasza baza danych była monitorowana, aby określić, czy mamy do czynienia z problemem wydajnościowym i aby móc go rozwiązać tak szybko, jak to możliwe. Do tego zadania istnieje kilka narzędzi takich jak m.in. Nagios, ClusterControl czy Zabbix, które pozwalają nam nie tylko monitorować, ale niektóre z nich pozwalają na podjęcie proaktywnych działań zanim pojawi się problem. Dzięki ClusterControl, oprócz monitorowania, administracji i kilku innych narzędzi, możemy otrzymywać zalecenia dotyczące działań, które możemy podjąć po otrzymaniu alertów dotyczących wydajności. To pozwala nam mieć pomysł na rozwiązanie potencjalnych problemów.
Ten blog nie ma stanowić wyczerpującego przewodnika po tym, jak poprawić wydajność bazy danych. Mamy nadzieję, że daje jaśniejszy obraz tego, co może stać się ważne, i niektóre podstawowe parametry, które można skonfigurować. Nie wahaj się dać nam znać, jeśli przegapiliśmy jakieś ważne.