Dokładny pomiar fragmentacji tabel i indeksów
Używaj pgstattuple i zapytań szacujących, aby określić rozmiar martwej przestrzeni przed wyborem sposobu naprawy.
Dokładny pomiar fragmentacji tabel i indeksów to bezpłatna lekcja PostgreSQL Performance & Query Optimization na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej PostgreSQL Performance & Query Optimization, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs PostgreSQL Performance & Query Optimization zawiera 4 lekcji w sumie.
Dlaczego powstaje puchnięcie
PostgreSQL używa mechanizmu MVCC (wielowersyjnej kontroli współbieżności). Gdy wykonują Państwo UPDATE lub DELETE dla wiersza, jego stara wersja nie jest od razu usuwana. Staje się martwym krotką, która nadal zajmuje miejsce, dopóki VACUUM nie oznaczy jej jako możliwej do ponownego użycia.
- Puchnięcie = miejsce zajmowane przez martwe krotki oraz niewykorzystane wolne miejsce, którego tabela lub indeks już nie potrzebuje.
- Puchnięcie zwiększa rozmiar danych na dysku, spowalnia skanowania sekwencyjne i zmniejsza efektywność pamięci podręcznej.
- Indeksy również puchną: strony B-tree zachowują wskaźniki do martwych krotek sterty, dopóki nie zostaną wyczyszczone.
Przed wyborem rozwiązania (VACUUM, VACUUM FULL, pg_repack lub REINDEX) należy najpierw zmierzyć, jak duże jest rzeczywiste puchnięcie. Zgadywanie prowadzi do zbędnych i zakłócających prac utrzymaniowych.
Żywe a martwe krotki
Najtańszy pierwszy sygnał pochodzi ze zbieracza statystyk. pg_stat_user_tables śledzi szacunkową liczbę żywych i martwych krotek w każdej tabeli; dane te są aktualizowane przez ANALYZE i autovacuum.
n_live_tup— szacunkowa liczba żywych wierszy.n_dead_tup— szacunkowa liczba martwych wierszy oczekujących na oczyszczenie.- Wysoki stosunek
n_dead_tupsugeruje, że autovacuum nie nadąża.
Jest to szacunek, a nie pomiar z dokładnością do bajta, ale nic nie kosztuje i świetnie nadaje się do wstępnej selekcji.
SELECT relname,
n_live_tup,
n_dead_tup,
round(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_pct,
last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;Szacowanie a dokładny pomiar
Istnieją dwie grupy metod pomiaru puchnięcia, z których każda ma swoje kompromisy:
- Zapytania szacujące odczytują wyłącznie statystyki katalogowe (
pg_class,pg_statistic). Są szybkie i nie wymagają blokad, ale przybliżone — ich dokładność zależy od aktualności ANALYZE i założeń dotyczących szerokości kolumn. - pgstattuple fizycznie skanuje relację, aby policzyć dokładną liczbę bajtów żywych i martwych. Jest precyzyjne, ale na dużych tabelach intensywnie obciąża operacje wejścia/wyjścia.
Praktyczny sposób pracy: użyć taniego szacowania do znalezienia kandydatów, a następnie użyć pgstattuple do potwierdzenia najgorszych przypadków przed podjęciem działań naprawczych.
Instalowanie pgstattuple
pgstattuple to rozszerzenie contrib dostarczane razem z PostgreSQL. Przed użyciem należy je włączyć osobno w każdej bazie danych.
- Włączenie rozszerzenia wymaga uprawnień superużytkownika lub roli z uprawnieniem
CREATEdo bazy danych. - Do uruchamiania jego funkcji dla dowolnych relacji wymagana jest rola
pg_stat_scan_tables(lub uprawnienia superużytkownika).
Po zainstalowaniu dostępne są funkcje pgstattuple(), pgstatindex() oraz lżejsza pgstattuple_approx().
CREATE EXTENSION IF NOT EXISTS pgstattuple;Odczytywanie wyników pgstattuple
pgstattuple('relation') wykonuje pełne skanowanie i zwraca jeden wiersz zawierający dokładne informacje o stercie w bajtach.
table_len— całkowity rozmiar relacji w bajtach.tuple_count/tuple_len— liczba i łączny rozmiar żywych krotek.dead_tuple_count/dead_tuple_len— liczba martwych krotek i zajmowane przez nie bajty.free_space/free_percent— wolne miejsce możliwe do ponownego użycia.
Najważniejszym sygnałem puchnięcia jest dead_tuple_percent wraz z free_percent: wartości te pokazują łącznie, jaka część pliku nie zawiera żywych danych.
SELECT table_len,
tuple_count,
tuple_len,
dead_tuple_count,
dead_tuple_len,
dead_tuple_percent,
free_space,
free_percent
FROM pgstattuple('public.orders');Koszt pełnego skanowania
pgstattuple() odczytuje każdą stronę relacji. W przypadku tabeli o rozmiarze 500 GB oznacza to dużo operacji wejścia/wyjścia i może usunąć przydatne dane z pamięci podręcznej.
- Funkcja pobiera tylko blokadę ACCESS SHARE, więc nie blokuje odczytów ani zapisów — obciążenie operacjami wejścia/wyjścia jest jednak rzeczywiste.
- W przypadku dużych tabel należy preferować
pgstattuple_approx(), które korzysta z mapy widoczności, pomija strony w pełni widoczne i próbuje pozostałe. approxzwraca wartościapprox_free_percentidead_tuple_percentzbliżone do dokładnych, przy ułamku kosztu.
Praktyczna zasada: najpierw szacować, na tabelach średniej wielkości uruchamiać approx, a dokładne pgstattuple() stosować dopiero do ostatecznego potwierdzenia w przypadku konkretnego podejrzanego obiektu.
SELECT table_len,
approx_tuple_count,
approx_tuple_percent,
dead_tuple_count,
dead_tuple_percent,
approx_free_percent
FROM pgstattuple_approx('public.orders');Pomiar puchnięcia indeksów
Indeksy puchną niezależnie od tabeli, do której należą. W przypadku indeksów B-tree należy użyć pgstatindex(), aby uzyskać szczegółowe informacje o ich strukturze.
avg_leaf_density— odsetek stron liści wypełnionych użytecznymi danymi. Zdrowe indeksy osiągają wartości bliskie 90%; spadek w kierunku 50% sygnalizuje silne puchnięcie.leaf_fragmentation— stopień nieuporządkowania stron liści; wysoka fragmentacja pogarsza skanowania zakresów.index_sizeorazinternal_pages/leaf_pagesopisują strukturę drzewa.
Niska wartość avg_leaf_density jest najmocniejszym argumentem za użyciem REINDEX (najlepiej REINDEX ... CONCURRENTLY).
SELECT version,
index_size,
leaf_pages,
avg_leaf_density,
leaf_fragmentation
FROM pgstatindex('public.orders_customer_id_idx');Podejście oparte na zapytaniu szacującym
Gdy nie można sobie pozwolić na skanowanie, społecznościowe zapytanie szacujące puchnięcie (z check_postgres / pgsql-bloat-estimation) oblicza oczekiwany rozmiar na podstawie statystyk i porównuje go z rzeczywistym rozmiarem.
Jego podstawowa idea jest następująca:
- Pobrać średnią szerokość wiersza z
pg_statistic(wartośćavg_widthdla każdej kolumny zarejestrowaną przez ANALYZE). - Dodać narzut nagłówka krotki i wyrównania, a następnie podzielić rozmiar tabeli przez oczekiwaną liczbę krotek na stronę.
- Różnica między oczekiwaną a rzeczywistą liczbą stron to szacowane puchnięcie.
Wynik jest przybliżony i wrażliwy na nieaktualne statystyki, ale zapytanie wykonuje się w milisekundach dla całej bazy danych.
Dlaczego szacunki się rozjeżdżają
Dokładność szacowania gwałtownie spada, gdy dane wejściowe są nieprawidłowe. Należy uważać na następujące pułapki:
- Nieaktualne statystyki: jeśli ANALYZE nie uruchamiano niedawno, wartości
avg_widthi liczby wierszy są nieaktualne. Przed zaufaniem szacunkom należy uruchomićANALYZE. - Szerokie kolumny o zmiennej długości: duża zmienność szerokości wartości
text/jsonbsprawia, że średnie dla wierszy są niewiarygodne. - TOAST: duże wartości przechowywane poza wierszem znajdują się w osobnej tabeli TOAST; szacunki dla sterty całkowicie pomijają to miejsce.
- Fillfactor: tabela utworzona z
fillfactor < 100celowo pozostawia wolne miejsce — nie jest to puchnięcie.
Zaskakujący wynik szacowania należy zawsze zweryfikować za pomocą pgstattuple_approx() przed podjęciem działań.
ANALYZE public.orders;Nie należy zapominać o tabeli TOAST
Duże wartości kolumn są przenoszone do ukrytej tabeli TOAST, która puchnie niezależnie. Sterta może wyglądać na uporządkowaną, podczas gdy powiązana z nią relacja TOAST jest ogromna.
- Relację TOAST można znaleźć za pomocą
pg_class.reltoastrelid. - Należy uruchomić
pgstattuple()bezpośrednio dla identyfikatora OID relacji TOAST, aby zmierzyć zajmowane przez nią martwe miejsce.
Tabele z często aktualizowanymi kolumnami jsonb lub bytea często ukrywają większość puchnięcia właśnie w TOAST.
SELECT c.relname,
pg_size_pretty(pg_relation_size(c.reltoastrelid)) AS toast_size,
t.dead_tuple_percent
FROM pg_class c
CROSS JOIN LATERAL pgstattuple(c.reltoastrelid) AS t
WHERE c.relname = 'orders'
AND c.reltoastrelid <> 0;Od liczb do decyzji
Po uzyskaniu dokładnych danych należy przypisać je do odpowiedniej ścieżki naprawczej:
- wysokie dead_tuple_percent i wysokie free_percent: autovacuum nie nadąża — zwykłe
VACUUM(lub dostrojenie autovacuum) zazwyczaj odzyska miejsce możliwe do ponownego użycia bez przepisywania pliku; - bardzo wysokie free_percent, ale tabela się nie zmniejsza: plik zawiera wolne miejsce na końcu, którego VACUUM nie może zwrócić systemowi operacyjnemu — należy rozważyć
pg_repack(online) lubVACUUM FULL(blokuje tabelę); - niska wartość Index avg_leaf_density:
REINDEX CONCURRENTLY.
Należy ustawić próg (np. działać dopiero powyżej około 20% puchnięcia i przy znaczącym rozmiarze bezwzględnym), aby nie uruchamiać zakłócających prac utrzymaniowych dla niewielkich korzyści.
Szybkie sprawdzenie
Podejrzewają Państwo, że tabela o rozmiarze 400 GB jest silnie spuchnięta, i chcą uzyskać dokładną wartość puchnięcia przy jak najmniejszym wpływie operacji wejścia/wyjścia, zakładając, że autovacuum dość regularnie aktualizuje mapę widoczności. Które narzędzie będzie najlepsze?
Podsumowanie
Mają już Państwo warstwową metodę określania puchnięcia przed podjęciem działań:
- Wstępna selekcja za pomocą
pg_stat_user_tables(stosunek n_dead_tup) — bezpłatna i natychmiastowa. - Szacowanie dla całej bazy danych za pomocą zapytań opartych na statystykach — szybkie, ale wymagające weryfikacji za pomocą aktualnego
ANALYZE. - Precyzyjne potwierdzenie za pomocą
pgstattuple()lubpgstattuple_approx()dla dużych tabel, z odczytem dead_tuple_percent i free_percent. - Indeksy: należy używać
pgstatindex()i obserwowaćavg_leaf_densityorazleaf_fragmentation. - Nie należy zapominać o TOAST ani pomijać celowo pozostawionego wolnego miejsca wynikającego z fillfactor.
Dopiero gdy wartości przekroczą istotny próg, należy wybrać VACUUM, pg_repack, VACUUM FULL lub REINDEX — pomiary wyznaczają działania naprawcze, nigdy odwrotnie.
Ucz się SQL dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 22
- Lekcje
- 88
Często zadawane pytania
Czy lekcja „Dokładny pomiar fragmentacji tabel i indeksów” jest bezpłatna?
Tak — pełny tekst „Dokładny pomiar fragmentacji tabel i indeksów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu PostgreSQL Performance & Query Optimization, przejdź na CoddyKit PRO. Kurs PostgreSQL Performance & Query Optimization zawiera 4 lekcji w sumie.
Co nauczysz się w „Dokładny pomiar fragmentacji tabel i indeksów”?
Używaj pgstattuple i zapytań szacujących, aby określić rozmiar martwej przestrzeni przed wyborem sposobu naprawy. Ćwiczysz PostgreSQL Performance & Query Optimization z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć PostgreSQL Performance & Query Optimization?
Nie wymagamy żadnego doświadczenia. PostgreSQL Performance & Query Optimization w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Dokładny pomiar fragmentacji tabel i indeksów”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji PostgreSQL Performance & Query Optimization?
Tak. Każda lekcja PostgreSQL Performance & Query Optimization zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Dokładny pomiar fragmentacji tabel i indeksów
- Odzyskiwanie miejsca za pomocą pg_repack
- Dostrajanie fillfactor dla tabel intensywnie aktualizowanych
- Wewnętrzne działanie TOAST i przechowywanie dużych wartości