PostgreSQL nie korzysta z mechanizmu aktualizacji NA MIEJSCU, więc zgodnie ze sposobem, w jaki zaprojektowano polecenia DELETE i UPDATE,
- Za każdym razem, gdy wykonywane są operacje DELETE, oznacza on istniejącą krotkę jako DEAD zamiast fizycznie usuwać te krotki.
- Podobnie, za każdym razem, gdy wykonywana jest operacja UPDATE, oznacza odpowiednią istniejącą krotkę jako DEAD i wstawia nową krotkę (tj. operacja UPDATE =DELETE + INSERT).
Więc każde polecenie DELETE i UPDATE da w wyniku jedną krotkę DEAD, która nigdy nie zostanie użyta (chyba że istnieją transakcje równoległe). Te martwe krotki doprowadzą do niepotrzebnego dodatkowego wykorzystania miejsca, nawet jeśli liczba efektywnych rekordów jest taka sama lub mniejsza. W PostgreSQL nazywa się to również powiększaniem przestrzeni. Ponieważ PostgreSQL jest szeroko stosowany jako rodzaj relacyjnego systemu bazodanowego OLTP, w którym często wykonywane są operacje INSERT, UPDATE i DELETE, będzie wiele martwych krotek, a co za tym idzie, odpowiadających im konsekwencji. Dlatego PostgreSQL wymagał silnego mechanizmu konserwacji, aby poradzić sobie z tymi krotkami DEAD. VACUUM to proces konserwacji, który zajmuje się radzeniem z krotką DEAD wraz z kilkoma dodatkowymi czynnościami przydatnymi do optymalizacji działania VACUUM. Przyjrzyjmy się terminologii, która będzie używana w dalszej części tego bloga.
Mapa widoczności
Jak sama nazwa wskazuje, przechowuje informacje o widoczności stron zawierających tylko krotki, o których wiadomo, że są widoczne dla wszystkich aktywnych transakcji. Dla każdej strony używany jest jeden bit. Jeśli bit jest ustawiony na 1, wszystkie krotki z odpowiedniej strony są widoczne. Bit ustawiony na 0 oznacza, że na danej stronie nie ma wolnego miejsca, a krotki mogą być widoczne dla wszystkich transakcji.
Mapa widoczności jest utrzymywana dla każdej relacji (tabeli i indeksu) i jest kojarzona z głównymi relacjami, tj. jeśli nazwa węzła pliku relacji to 12345, to plik widoczności jest przechowywany w pliku równoległym 12345_vm.
Mapa wolnej przestrzeni
Utrzymuje informacje o wolnej przestrzeni zawierające szczegółowe informacje o dostępnej przestrzeni w relacji. Jest to również przechowywane w pliku równoległym do głównego pliku relacji, tj. jeśli nazwa węzła pliku relacji to 12345, wówczas plik mapy wolnej przestrzeni jest przechowywany w pliku równoległym 12345_fsm.
Zatrzymaj krotkę
PostgreSQL używa 4 bajtów do przechowywania identyfikatora transakcji, co oznacza, że można wygenerować maksymalnie 2 miliardy transakcji, zanim zostanie on zawinięty. Rozważmy teraz, że pewna krotka zawiera początkowy identyfikator transakcji, powiedzmy 100, a następnie dla nowej transakcji (która wykorzystuje transakcję opakowaną) powiedzmy 5, id transakcji 100 będzie patrzeć w przyszłość i nie będzie w stanie zobaczyć dodanych danych /zmodyfikowane przez to, mimo że faktycznie było to w przeszłości. Aby uniknąć tego specjalnego identyfikatora transakcji, przypisywany jest FrozenTransactionId (równy 2). Ten specjalny identyfikator transakcji jest zawsze uważany za przeszły i będzie widoczny dla wszystkich transakcji.
PRÓŻNIA
Głównym zadaniem VACUUM jest odzyskanie przestrzeni dyskowej zajmowanej przez krotki DEAD. Odzyskana przestrzeń dyskowa nie jest zwracana do systemu operacyjnego, a raczej jest defragmentowana w ramach tej samej strony, dzięki czemu jest dostępna do ponownego wykorzystania przy przyszłym wstawianiu danych w tej samej tabeli. Podczas gdy operacja VACUUM jest wykonywana na określonym stole, jednocześnie na tym samym stole może być wykonywana inna operacja ODCZYTU/ZAPISU, ponieważ blokada na wyłączność nie jest wykonywana na danym stole. W przypadku, gdy nazwa tabeli nie zostanie podana, VACUUM zostanie wykonane na wszystkich tabelach bazy danych. Operacja VACUUM wykonuje poniżej serię operacji w ramach blokady ShareUpdateExclusive:
- Skanuj wszystkie strony wszystkich tabel (lub określonej tabeli) bazy danych, aby uzyskać wszystkie martwe krotki.
- W razie potrzeby zamroź stare krotki.
- Usuń krotkę indeksu wskazującą na odpowiednie krotki DEAD.
- Usuń DEAD krotki strony odpowiadające określonej tabeli i ponownie przydziel aktywne krotki na stronie.
- Aktualizuj mapę wolnego miejsca (FSM) i mapę widoczności (VM).
- Obetnij ostatnią stronę, jeśli to możliwe (jeśli były martwe krotki, które zostały uwolnione).
- Zaktualizuj wszystkie odpowiadające tabele systemowe.
Jak widać z powyższych kroków pracy dla VACUUM widać, że jest to bardzo kosztowna operacja, ponieważ wymaga przetworzenia wszystkich stron relacji. Dlatego bardzo potrzebne jest pomijanie ewentualnych stron, które nie wymagają odkurzania. Ponieważ mapa widoczności (VM) podaje informacje o stronie, na której, jeśli nie ma wolnego miejsca, można założyć, że odpowiednia próżnia strony nie jest wymagana, a zatem tę stronę można bezpiecznie pominąć.
Ponieważ VACUUM i tak przechodzi przez wszystkie strony i ich wszystkie krotki, więc wykorzystuje okazję do wykonania innego ważnego zadania polegającego na zamrożeniu kwalifikujących się krotek.
Pełna próżnia
Jak omówiono w poprzedniej sekcji, mimo że VACUUM usuwa wszystkie krotki DEAD i defragmentuje stronę do przyszłego użytku, nie pomaga to w zmniejszeniu ogólnego przechowywania tabeli, ponieważ miejsce w rzeczywistości nie jest zwalniane do system operacyjny. Załóżmy, że w tabeli tbl1 całkowita pamięć osiągnęła 1,5 GB iz tego 1 GB jest zajęte przez martwą krotkę, a po VACUUM kolejne około 1 GB będzie dostępne do dalszego wstawiania krotek, ale nadal całkowita pamięć pozostanie na poziomie 1,5 GB.
Pełna PRÓŻNIA rozwiązuje ten problem, faktycznie zwalniając miejsce i przywracając je z powrotem do systemu operacyjnego. Ale to ma swoją cenę. W przeciwieństwie do VACUUM, FULL VACUUM nie pozwala na pracę równoległą, ponieważ zajmuje wyłączną blokadę relacji, która zostaje PEŁNA VACUUM. Poniżej znajdują się kroki:
- Przyjmuje wyłączną blokadę relacji.
- Utwórz równoległy pusty plik pamięci.
- Skopiuj wszystkie aktywne krotki z bieżącej pamięci do nowo przydzielonej pamięci.
- Następnie zwolnij oryginalną pamięć.
- Zwolnij zamek.
Więc jak jasno wynika z kroków, będzie miał miejsce na przechowywanie tylko pozostałych danych.
Automatyczna próżnia
Zamiast robić VACUUM ręcznie, PostgreSQL obsługuje demona, który okresowo automatycznie uruchamia VACUUM. Za każdym razem, gdy VACUUM budzi się (domyślnie 1 minuta) wywołuje wiele prac (w zależności od konfiguracji procesów autovacuum_worker).
Pracownicy automatycznej próżni wykonują procesy VACUUM jednocześnie dla odpowiednich wyznaczonych tabel. Ponieważ VACUUM nie blokuje na wyłączność tabel, nie wpływa (lub minimalnie) na inne działanie bazy danych.
Konfiguracja Auto-VACUUM powinna być wykonana na podstawie wzorca użytkowania bazy danych. Nie powinno to być zbyt częste (ponieważ spowoduje to marnowanie przebudzenia pracownika, ponieważ może nie być lub za mało martwych krotek) ani zbyt dużo opóźnione (spowoduje to wiele martwych krotek razem, a tym samym rozdęcie tabeli).
PRÓŻNIA lub pełna PRÓŻNIA
W idealnym przypadku aplikacja bazodanowa powinna być zaprojektowana w taki sposób, aby nie było potrzeby stosowania PEŁNEJ PRÓŻNI. Jak wyjaśniono powyżej, PEŁNA PRÓŻNIA odtwarza miejsce do przechowywania i odkłada dane, więc jeśli jest tylko mniej martwych krotek, natychmiast zostanie odtworzone miejsce do przechowywania, aby przywrócić wszystkie oryginalne dane. Również ponieważ FULL VACUUM blokuje tabelę na wyłączność, blokuje wszystkie operacje na odpowiedniej tabeli. Dlatego wykonywanie PEŁNEJ PRÓŻNI może czasami spowolnić całą bazę danych.
Podsumowując Pełnej PRÓŻNI należy unikać, chyba że wiadomo, że większość przestrzeni dyskowej jest z powodu martwych krotek. Rozszerzenie PostgreSQL pg_freespacemap może być użyte, aby uzyskać uczciwą wskazówkę na temat wolnego miejsca.
Zobaczmy przykład wyjaśnionego procesu VACUUM.
Najpierw utwórzmy prezentację tabeli1:
postgres=# create table demo1(id int, id2 int);
CREATE TABLE
I wstaw tam jakieś dane:
postgres=# insert into demo1 values(generate_series(1,10000), generate_series(1,
10000));
INSERT 0 10000
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
45 | 0.00
(1 row)
Teraz usuńmy dane:
postgres=# delete from demo1 where id%2=0;
DELETE 5000
I uruchom ręczne odkurzanie:
postgres=# vacuum demo1;
VACUUM
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
45 | 45.07
(1 row)
Ta wolna przestrzeń jest teraz dostępna do ponownego wykorzystania przez PostgreSQL, ale jeśli chcesz zwolnić tę przestrzeń dla systemu operacyjnego, uruchom:
postgres=# vacuum full demo1;
VACUUM
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
23 | 0.00
(1 row)
Wniosek
I to był krótki przykład działania procesu VACUUM. Na szczęście, dzięki procesowi automatycznego odkurzania, przez większość czasu i we wspólnym środowisku PostgreSQL, nie musisz o tym myśleć, ponieważ jest zarządzany przez sam silnik.