
Tworzenie kopii zapasowych za pomocą PG_DUMP czy PG_DUMPALL pozwala nam przywrócić stan bazy danych do punktu, w którym ta kopia została wykonana. Oznacza to, że jeśli np. wykonujemy taką kopię zapasową w każdą niedzielę, a awaria klastra nastąpi w sobotę, będziemy w stanie odzyskać bazę danych w stanie sprzed niemal tygodnia. Podobnie sprawy się mają z zimnym backupem na poziomie fizycznym. W większości poważnych zastosowań taki system kopii zapasowych nie sprawdzi się. Wystarczy wyobrazić sobie utratę zmian z ostatniego tygodnia w banku... Oczywiście można robić zrzuty za pomocą PG_DUMP czy PG_DUMPALL częściej, ale zawsze będzie to kopia z archiwalnego punktu w czasie. Ponadto jest to kopia logiczna, nie pozwoli odzyskać plików danych w pierwotnym kształcie ani konfiguracji na poziomie klastra.
Możemy również zrobić zimny backup, wyłączając serwer i kopiując całą zawartość PGDATA (i ewentualnie innych katalogów związanych z przestrzeniami tabel leżącymi poza PGDATA). Będzie to kopia fizyczna wszystkich plików, włącznie z plikami konfiguracyjnymi. W razie potrzeby możemy odzyskać stan danych z momentu utworzenia takiej kopii zapasowej, przywracając pliki w pierwotne położenie. Jednak także w tym przypadku jesteśmy w stanie odzyskać dane do przeszłego punktu w czasie.
Na szczęście istnieje inna metoda tworzenia i odzyskiwania kopii zapasowych. Jest ona oparta o archiwizację ciągłą. Pliki WAL przechowują historię wszystkich zmian na klastrze (poza zmianami parametrów bezpośrednio wykonywanymi na plikach konfiguracyjnych jak postgresql.conf czy pg_hba.conf). Gdybyśmy posiadali fizyczną kopię plików klastra z jakiegoś punktu w czasie oraz wszystkie pliki WAL od momentu utworzenia kopii zapasowej do teraz, moglibyśmy odtworzyć klaster do dowolnego punktu w tej przestrzeni czasowej. Wystarczyłoby przywrócić stan plików do punktu, z którego pochodzi backup, a następnie powtórzyć wszystkie czynności zapisane w plikach WAL do wskazanego punktu w czasie, w tym - chwili tuż przed awarią.
Pliki WAL rotują w ramach limitu określonego przez parametr "min_wal_size". Przechowywane są tylko te potrzebne do ewentualnego awaryjnego odtworzenia operacji od ostatniego checkpointa. Z pomocą przychodzi nam archiwizacja ciągła, która sprowadza się w zasadzie do kopiowania plików WAL do innej lokalizacji przed ich usunięciem z katalogu "pg_wal". Aby mechanizm ten zadziałał poprawnie, trzeba będzie włączyć archiwizację ciągłą jeszcze przed utworzeniem kopii zapasowej.
Pamiętajmy, że kopie zapasowe na poziomie fizycznym dotyczą całego klastra. Nie ma możliwości zrobienia kopii zapasowej wybranej bazy w ten sposób ani odtwarzania baz danych pojedynczo.
Aby włączyć archiwizację ciągłą, musimy zmodyfikować dwa parametry: "archive_mode" i "archive_command". W starszych wersjach PostgreSQL trzeba jeszcze było przestawić parametr "wal_level" na "archive", od wersji 13 jest już ustawiony na "replika" - wystarczający zarówno do archiwizacji ciągłej jak i replikacji. Przestawienie "archive_mode" na "on" powoduje, że klaster zacznie archiwizować pliki WAL za pomocą komendy umieszczonej w "archive_command". W pierwszej zatem kolejności musimy włączyć "archive_mode":
alter system set archive_mode=on;
Przestawienie tego parametru wiąże się z koniecznością zrestartowania serwera:
pg_ctl -D /data_pg/ restart
Pozostaje nam do ustawienia "archive_command", czyli komenda archiwizująca pliki WAL. Będą one musiały być gdzieś odkładane, zatem musimy utworzyć katalog z odpowiednimi uprawnieniami (katalog ten tworzymy jako użytkownik systemowy z prawami do sudo):
sudo mkdir /archs
sudo chown postgres:postgres /archs/
Do czasu skonfigurowania parametru "archive_command" na niepusty ciąg archiwizacja nie będzie jeszcze działać.
Komenda określona w parametrze "archive_command" będzie wykonywana natychmiast po zamknięciu pliku WAL. Może to być dowolna komenda, cp, rsync czy scp w zależności od wybranego miejsca docelowego dla archiwizacji. W tym przypadku mamy stworzony lokalnie katalog na zarchiwizowane pliki WAL, w związku z czym posłużymy się linuksową komendą cp służącą do kopiowania plików. W parametrze tym wystąpić mogą dwie zmienne: "%p" i "%f". "%p" odnosi się do nazwy pliku WAL wraz ze ścieżką, "%f" do samej nazwy pliku WAL. Konfigurujemy ten parametr tak, by kopiował pliki WAL właśnie do stworzonego katalogu "archs":
alter system set archive_command='rsync %p /archs/%f';
select pg_reload_conf();
Warto przechowywać katalog ze zarchiwizowanymi plikami WAL na innym dysku niż sam klaster, dotyczy to również kopii zapasowych.
W przypadku parametru "archive_command" wystarczy przeładowanie konfiguracji. Nawet jeśli nie planujemy używać archiwizacji warto ją włączyć i ustawić “archive_command” na '/bin/true' Dzięki temu włączenie archiwizacji w przyszłości będzie mogło się odbywać online, bez potrzeby restartowania klastra (wymaganego przy zmianie “archive_mode”). “/bin/true” zwróci nam zawsze wartość “true” co dla PostgreSQL jest sygnałem, że archiwizacja WALa przebiegła pomyślnie. Gdybyśmy włączyli archiwizację a pozostawili puste “archive_command”, PostgreSQL nie dostawałby informacji o poprawnej archiwizacji WALa i nie usuwałby plików WAL z pg_wal, co skutkowałoby w efekcie zapełnieniem dysku.
Od momentu ustawienia “archive_command” każdy plik WAL przed skasowaniem w wyniku rotacji zostanie skopiowany do naszego katalogu "archs". Możemy się upewnić że archiwizacja działa, powodując jakąś operację generującą dużo wpisów do plików WAL. Tworzymy tabelę i zapełnimy ją danymi:
create table archiwizacja(x integer,y text);do
$$
begin
for x in 1..1000000 loop
insert into archiwizacja values (x,'element numer '||x);
end loop;
end $$;
Po tej operacji możemy zajrzeć do katalogu "archs". Powinniśmy już widzieć utworzone zarchiwizowane pliki WAL:
ls -latr /archs/
W tym przypadku utworzone zostało 5 plików (*16MB). Co się jednak stanie, gdy komenda określona w "archive_command" będzie wadliwa? Może się okazać, że nie ma możliwości jej wykonania, bo np. nie mamy uprawnień do katalogu, do którego mają być archiwizowane pliki WAL. W takiej sytuacji PostgreSQL nie będzie kasował plików z "pg_wal" tak długo, aż operacja archiwizacji się nie powiedzie. Sprawdźmy to w praktyce. Sprawdzamy, ile zajmują moje aktualne pliki WAL w "pg_wal":
du -sh /data_pg/pg_wal/
Na ten moment bieżące pliki WAL zajmują 833MB miejsca na dysku. Ustawienie parametru "max_wal_size" wskazuje na max 1GB na pliki WAL. Limit ten jest limitem "miękkim", co może powodować, że suma wielkości plików WAL może być większa niż wartość określona w “max_wal_size”. Wartość w tym parametrze to tylko “trigger” wywołujący checkpoint kiedy nazbiera się plików WAL więcej niż limit w “max_wal_size”. Na potrzeby naszego testu działającej niepoprawnie archiwizacji modyfikujemy "archive_command" tak, by PostgreSQL usiłował kopiować pliki WAL do nieistniejącego katalogu:
alter system set archive_command='rsync %p /nieistniejacy_katalog/%f';
select pg_reload_conf();
Następnie ładujemy dużą liczbę danych do wcześniej utworzonej tabeli (wcześniej ładowaliśmy do niej milion wierszy, teraz 100 milionów):
do
$$
begin
for x in 1..100000000 loop
insert into archiwizacja values (x,'element numer '||x);
end loop;
end $$;
Istotne jest to, by był to wolumen danych na tyle duży, aby zdążył nadpisać wszystkie istniejące w "pg_wal" pliki WAL. Oznacza to, że musimy wygenerować w tym przypadku co najmniej 1.1GB danych do plików WAL. W trakcie wykonywania tego skryptu możemy zajrzeć do loga klastra. Będzie informował na temat problemu z archiwizacją:
Po pewnym czasie w związku z brakiem możliwości kopiowania plików WAL do "/archs" nasz katalog "pg_wal" mocno urósł:
du -sh /data_pg/pg_wal/
Pamiętajmy, by przywrócić "archive_command" do właściwej postaci:
alter system set archive_command='rsync %p /archs/%f';
select pg_reload_conf();
Narzędzie pg_basebackup pozwala wykonać gorącą kopię zapasową. Czym się różni gorąca kopia zapasowa od zimnej? Tym, że gorąca kopia zapasowa nie wymaga zatrzymywania klastra w celu utworzenia kopii zapasowej. Mimo że klaster będzie działał w trakcie wykonywania kopii zapasowej, sama kopia będzie spójna. Ponadto, mając pliki WAL od momentu zrobienia gorącej kopii zapasowej do teraz, możemy przywrócić stan klastra do dowolnego momentu pomiędzy tymi dwoma punktami. Narzędzie pg_basebackup potrafi również tworzyć kopie zapasowe ze zdalnego serwera po sieci. Stosowanie gorących kopii zapasowych z użyciem pg_basebackup ma znaczącą przewagę nad stosowaniem kopii zimnych.
Aby wykorzystać to narzędzie, musimy najpierw utworzyć pusty katalog, w którym znajdzie się kopia zapasowa. Katalog ten musi być pusty, ponieważ jeśli nie będzie, przy próbie wykonania kopii zapasowej dostaniemy taki komunikat:
pg_basebackup: error: directory "/backups/backup_1" exists but is not empty
Tworzymy zatem odpowiedni katalog i zmieniamy właściciela na użytkownika postgres (jako użytkownik systemowy z prawami do sudo):
sudo mkdir /backups
sudo mkdir /backups/backup_1
sudo chown -R postgres:postgres /backups/
Zanim przystąpimy do backupu, możemy skasować tabelę "archiwizacja", którą tworzyliśmy przy okazji omawiania włączania archiwizacji ciągłej. Zajmuje ona niemal 6GB i kopiowanie tych danych potrwa długo:
drop table archiwizacja;
Najprostsze możliwe wywołanie pg_basebackup będzie wyglądać tak (jako użykownik systemowy postgres):
pg_basebackup -D /backups/backup_1/
Przełącznikiem -D wskazujemy katalog docelowy, a nie naszą PGDATA! Pg_basebackup domyślnie będzie się łączył do lokalnego klastra po porcie 5432, chyba że wskażemy inaczej parametrami -h i -p. Zajmiemy się tym nieco później. Po wykonaniu tej komendy w naszym docelowym katalogu powinna znaleźć się kopia naszego PGDATA:
Co się stanie, jeśli spróbujemy zrobić kopię zapasową klastra, a okaże się, że mamy przestrzenie tabel w innych lokalizacjach niż PGDATA? Przeprowadźmy taki eksperyment. Stwórzmy nowy katalog, stwórzmy w nim przestrzeń tabel, a następnie umieśćmy w niej bazę danych. Po tym wszystkim spróbujemy stworzyć kopię zapasową (zaczynajmy jako użytkownik systemowy z prawami do sudo):
sudo mkdir /other_disk
sudo chown postgres:postgres /other_disk/
sudo su - postgres
psql
create tablespace other_disk location '/other_disk';
create database on_other_disk tablespace other_disk;
Stworzymy teraz nowy katalog i ponowimy próbę backupu do tego nowego katalogu (jako użytkownik systemowy postgres):
mkdir /backups/backup_2
pg_basebackup -D /backups/backup_2/
Przy próbie wykonania backupu dostajemy taki komunikat:
Wynika to z tego, że pg_basebackup usiłuje umieścić backup tej nowej przestrzeni tabel w tym samym miejscu, w którym ta przestrzeń się znajduje. Katalog ten oczywiście nie jest pusty i stąd błąd. Backup nie został wykonany. Aby nasz backup się wykonał, musimy wskazać nowe miejsce docelowe dla katalogów naszych przestrzeni tabel np:
pg_basebackup -D /backups/backup_2/ --tablespace-mapping=/other_disk=/backups/backup_2/other_disk
Tym razem backup udało się wykonać bez problemu. Backup przestrzeni z "/other_disk" został umieszczony w "/backups/backup_2/other_disk":
ls /backups/backup_2/other_disk/
Za pomocą pg_basebackup możemy też wykonać kopię zapasową zdalnego serwera, musimy jednak dodać w tym celu wpis w "pg_hba.conf". W tym przypadku łączymy się do tego samego hosta, dlatego adres IP powtarza się, ale to bez znaczenia, bo działa to tak samo, jakbym łączył się do innego hosta. Mój wpis w "pg_hba_conf":
host replication postgres 13.81.53.1/32 scram-sha-256
Po dodaniu wpisu przeładowujemy konfigurację:
psql -c "select pg_reload_conf()"
Jeśli wykonujemy wszystkie polecenia z tego rozdziału, to pamiętajmy, że mamy utworzoną przestrzeń tabel w katalogu "/other_disk", a w niej bazę "on_other_disk". Możemy je albo usunąć poniższymi poleceniami, albo użyć przełącznika "--tablespace-mapping":
psql -c "drop database on_other_disk"
psql -c "drop tablespace other_disk"
Potrzebujemy jeszcze katalogu na nasz kolejny backup:
mkdir /backups/backup_3
I możemy przystąpić do wykonywania kopii zapasowej. Aby wskazać IP źródłowego serwera, używamy przełącznika "-h":
pg_basebackup -D /backups/backup_3/ -h 13.81.53.1
Przed rozpoczęciem backupu poproszą nas o hasło. Jeśli wykonujemy backup ręcznie, to nie ma problemu. Gorzej jeśli chcielibyśmy takie wywołanie umieścić np. w cronie, by backup wykonywał się cyklicznie, wtedy pojawi się problem. Nie będzie możliwości wprowadzenia hasła. Aby rozwiązać ten problem, musimy dodać wpis do pliku .pgpass użytkownika postgres. W tym celu edytujemy plik .pgpass:
nano ~/.pgpass
Ogólna struktura wpisu w tym pliku wygląda tak:
ip_hosta_którego_robimy_backup:port:baza_danych:użytkownik:hasło
W naszym przypadku w miejsce bazy danych wprowadzamy "replication":
13.81.53.1:5432:replication:postgres:uwielbiam_bigos_123
Możemy teraz utworzyć katalog potrzebny do kolejnej próby wykonania backupu po sieci i wykonać backup. Tym razem przebiega to bez problemu:
mkdir /backups/backup_4
pg_basebackup -D /backups/backup_4/ -h 13.81.53.1
Wykonywanie kopii zapasowej może długo trwać, dlatego dobrze jest monitorować jego postępy. W tym celu możemy użyć przełącznika -P, który spowoduje, że pg_basebackup będzie nam pokazywał procent wykonanej pracy. Działa to w ten sposób, że na początku sprawdzana jest wielkość plików klastra i na tej podstawie estymowany jest czas. Pamiętajmy, że to tylko estymacja, w tle mogą np. zostać uruchomione inne procesy zjadające dużo I/O i opóźniające wykonanie backupu. Mogą też być do klastra dodawane dane w trakcie wykonywania kopii zapasowej:
mkdir /backups/backup_5
pg_basebackup -D /backups/backup_5/ -P
Możemy też skompresować backup w locie, korzystając z przełączników "-Ft" i "-z". Pierwszy powoduje stworzenie archiwum ".tar", drugi powoduje kompresję:
mkdir /backups/backup_6
pg_basebackup -D /backups/backup_6/ -Ft -z
Poziom kompresji jest całkiem przyzwoity. PGDATA u mnie zajmuje 1.3GB, a skompresowany backup zaledwie 43MB.
Checkpoint może trwać, zależnie od naszego ustawienia "checkpoint_completion_target". Możemy wymusić szybki checkpoint (powodując jednak chwilowy wzrost obciążeń I/O) za pomocą przełącznika "--checkpoint=fast":
mkdir /backups/backup_7
pg_basebackup --checkpoint=fast -D /backups/backup_7/
Przy planowaniu strategii kopii zapasowych trzeba wziąć pod uwagę liczbę generowanych zarchiwizowanych plików WAL. Aby odtworzyć klaster w razie awarii, będziemy potrzebowali jego kopii zapasowej oraz wszystkich plików WAL od momentu startu backupu do teraz. To z kolei wpływa na liczbę przechowywanych plików WAL oraz czas odtwarzania. Im większe odstępy pomiędzy backupami, tym więcej plików WAL musimy przechowywać i tym dłużej będzie trwało ewentualne odtwarzanie. PostgreSQL sam nie kasuje żadnych plików WAL. Jeśli więc nie zadbamy o to samodzielnie, w krótkim czasie te pliki mogą zająć całą dostępną przestrzeń na dysku.
Aby bezpiecznie usunąć niepotrzebne pliki WAL musimy określić graniczny punkt czasowy określający, które pliki WAL nie są nam już potrzebne. Za niepotrzebne uznajemy to poprzedzające wykonanie kopii zapasowej. Podczas tworzenia kopii zapasowej tworzony jest plik z końcówką ".backup", a więc wszystkie WAL starsze niż ten plik można usunąć.
Aby kopia zapasowa była użyteczna, będziemy potrzebowali wszystkich plików WAL powstałych podczas wykonywania backupu i następujących po nim. Aby łatwiej określić, które pliki WAL są potrzebne, tworzony jest właśnie plik z końcówką ".backup". Dotyczy to oczywiście zarówno zarchiwizowanych już plików WAL, jak i tych bieżących znajdujących się w "pg_wal". Jego nazwa bierze się od pierwszego pliku WAL, który jest niezbędny do odtworzenia bazy z danego backupu. Jeśli plik ten nazywa się np. "000000010000000F00000058.00000028.backup", oznacza to, że pierwszym niezbędnym do odtwarzania plikiem WAL jest plik o nazwie "000000010000000F00000058". Druga część nazwy - w tym przypadku "00000028"- określa offset w tym właśnie pliku WAL. Wszystkie pliki WAL o numerach mniejszych niż ten mogą zostać bezpiecznie usunięte.
W celu usunięcia niepotrzebnych już plików WAL posłużymy się aplikacją "pg_archivecleanup" instalowanym razem z innymi binarkami PostgreSQL. W pierwszej kolejności musimy określić, jakie mamy pliki ".backup":
ls /archs/ -la | grep backup
Najnowszy backup pochodzi z dzisiaj z godziny 13:18. Wszystkie starsze pliki WAL można więc usunąć. Wykorzystujemy "pg_archivecleanup", podając mu jako argumenty katalog ze zarchiwizowanymi plikami WAL oraz plik z rozszerzeniem ".backup", poniżej którego chcemy usunąć WALe:
pg_archivecleanup -d /archs/ 00000001000000070000005A.00000028.backup
Stworzymy teraz kopię zapasową, dokonamy zmian, które nie będą w niej uwzględnione i odzyskamy klaster wraz z tymi zmianami. Zaczniemy od wykonania kopii zapasowej. Forma wykonania backupu jest obojętna (ręczna kopia fizyczna plików czy pg_basebackup), wybieramy "pg_basebackup". Tworzymy katalog na kopię zapasową i wykonujemy backup.
mkdir /backups/backup_8
pg_basebackup -D /backups/backup_8/ -P
Posprzątamy jeszcze pliki WAL starsze niż nasza kopia zapasowa (dostosuj nazwę pliku do swojego stanu):
pg_archivecleanup -d /archs/ 000000010000000900000071.00000028.backup
Utworzymy tabelę i zapełnimy ją danymi. Nie będzie jej w kopii zapasowej, informacje o niej będą tylko w plikach WAL:
create table po_backupie(x integer,y text);do
$$
begin
for x in 1..1000000 loop
insert into po_backupie values (x,'element numer '||x);
end loop;
end $$;
Po stworzeniu tej tabeli zatrzymujemy klaster i kasujemy całą zawartość PGDATA:
pg_ctl -D /data_pg/ stop
rm -rf /data_pg/*
Przyszedł czas na odzysk. Po pierwsze, musimy spowodować, że pliki z backupu zostaną skopiowane do katalogu PGDATA. Po drugie, trzeba będzie zaimplementować wszystkie zmiany znajdujące się w plikach WAL od momentu zrobienia backupu do teraz. W pierwszej kolejności przywracamy backup:
cp -R /backups/backup_8/* /data_pg
Musimy teraz zaimplementować zmiany, które zawarte są w plikach WAL. W tym celu edytujemy plik "postgresql.conf" w odzyskanej PGDATA i odnajdujemy w nim parametr "restore_command".
nano /data_pg/postgresql.conf
Parametr ten służy do wskazania, skąd PostgreSQL ma sobie zasysać WALe. W naszym przypadku będzie to:
restore_command = 'rsync /archs/%f %p'
Gdzie "%f" stanowi nazwę pliku bez ścieżki, a "%p" ze ścieżką. PostgreSQL musi jednak skądś wiedzieć, że ma wejść w tryb odtwarzania. Sama zmiana parametru "restore_command" tego nie załatwi. W tym celu musimy utworzyć plik "recovery.signal" w naszej PGDATA. Plik ten może być pusty, istotny jest tylko fakt jego istnienia:
touch /data_pg/recovery.signal
Aby PostgreSQL nie wchodził w tryb odtwarzania za każdym startem, plik ten jest automatycznie usuwany po zakończeniu odtwarzania.
Tutaj od razu pewna uwaga. W wersjach PostgreSQL 11 i wcześniejszych tworzyło się plik "recovery.conf" w PGDATA i umieszczało w nim restore_command. W nowszych zostało to przeniesione do "postgresql.conf", podobnie jak parametry recovery_**** określające punkty docelowe stosowane przy odtwarzaniu do punktu w czasie. Jeśli stworzymy plik "recovery.conf" w PostgreSQL 12 lub wyższym, serwer nie wstanie, ale też nie rozpocznie odtwarzania.
Możemy teraz przejść do odtwarzania. O ile mamy poprawnie ustawione "restore_command" i istnieje plik recovery.signal w katalogu PGDATA, wystarczy teraz uruchomić serwer, by rozpoczęło się odtwarzanie:
pg_ctl -D /data_pg/ start
podejrzeć logi by zobaczyć co się dzieje w trakcie odtwarzania:
tail -f /data_pg/log/postgresql.log
W trakcie uruchamiania klastra powinniśmy zobaczyć mniej więcej coś takiego:
Zajrzyjmy jeszcze do tabeli, czy istnieje i czy zawiera nasze dane:
psql -c "select count(*) from po_backupie";
Jeśli problem z bazą danych sprowadza się do utraty spójności baz lub uszkodzonych plików, odzyskujemy ją w sposób opisany powyżej. Może się nam przytrafić również błąd ludzki, np. przypadkowe usunięcie jakiejś tabeli. W takiej sytuacji odtwarzanie "standardowe" nie sprawdzi się, ponieważ powtórzy wszystkie operacje z plików WAL włącznie z operacją kasowania tabeli. Najodpowiedniejsze w tym przypadku będzie odtwarzanie do punktu w czasie. Nie musimy cofać stanu całego klastra, możemy odtworzyć klaster do punktu w czasie "na boku" i stamtąd ewentualnie skopiować odzyskaną tabelę. Aby ta operacja była możliwa, potrzebujemy ostatniego backupu i plików WAL od tego backupu do minumum punktu, do którego chcemy przywracać. Zaczniemy od stworzenia kopii zapasowej:
mkdir /backups/pitr
pg_basebackup -D /backups/pitr/ -P --checkpoint=fast
Po tej operacji tworzymy i zapełniamy tabelę, którą zamierzamy usunąć:
create table test_backupu(x integer, y text);do
$$
begin
for x in 1..1000000 loop
insert into test_backupu values (x,'element numer '||x);
end loop;
end $$;
Sprawdzamy wielkość tabeli i liczbę wierszy w niej się znajdujących:
psql -c "select count(*) from po_backupie"
Tabela ma 57 MB i znajduje się w niej milion wierszy. Mamy godzinę 13:59:52, gdy stan tabeli jest taki, jaki powinien być:
Czekamy do 14:02 i wykonujemy dropa na tej tabeli:
psql -c "drop table test_backupu"
Sprawdzamy, czy na pewno jej nie ma:
Możemy teraz przejść do odtwarzania bazy. Najpierw upewnijmy się że parametr "restore_command" jest właściwie ustawiony w "postgresql.conf". W naszym przypadku wartość tego parametru wygląda tak:
restore_command = 'rsync /archs/%f %p'
Odtwarzanie zaczniemy od wyłączenia klastra:
pg_ctl -D /data_pg/ stop
Kopiujemy też pliki WAL do katalogu ze zarchiwizowanymi plikami WAL, ponieważ nie wiemy, czy tworzenie tabeli znajduje się w zarchiwizowanych już plikach WAL czy w bieżących.
cp /data_pg/pg_wal/* /archs/
Możemy teraz przejść do odtworzenia backupu w naszej PGDATA, trzeba ją jednak najpierw wyczyścić:
rm -rf /data_pg/*
Odtwarzamy teraz kopię zapasową do katalogu PGDATA:
cp -R /backups/pitr/* /data_pg/
Przyszedł czas na wskazanie punktu w czasie, do którego chcemy przywracać. Punkt docelowy możemy wskazać za pomocą etykiety backupu, czasu, id transakcji czy pliku WAL. My wskażemy czas.
W tym celu odkomentowujemy parametr "recovery_target_time" w pliku "postgresql.conf" i wprowadzamy wybrany punkt czasowy:
nano /data_pg/postgresql.conf
Jak widać w pliku postgresql.conf jako docelowy punkt odtwarzania możemy wskazać punkt w czasie, identyfikator transakcji, numer LSN czy nazwę punktu przywracania. Wprowadzamy taką wartość:
recovery_target_time = '2023-10-24 13:59:52.455783+00'
Tworzymy plik sygnału rozpoczęcia odtwarzania:
touch /data_pg/recovery.signal
Uruchamiamy klaster, mając w drugiej sesji podgląd logów:
pg_ctl -D /data_pg/ start
Po uruchomieniu w logu powinniśmy zobaczyć coś takiego:
Po zakończeniu odtwarzania PostgreSQL zawisa i czeka na dalsze instrukcje. W tej chwili możemy zmienić punkt docelowy odtwarzania (ale tylko do przodu) i zrestartować klaster, jeśli okazałoby się, że wycelowaliśmy w zbyt wczesny punkt. W tej chwili do czasu wywołania funkcji "pg_wal_replay_resume()" klaster jest otwarty, ale tylko do odczytu.
Jeśli nie zamierzamy zmieniać punktu odtwarzania, wywołujemy "pg_wal_replay_resume()":
psql -c "select pg_wal_replay_resume()"
Warto przy okazji zajrzeć do logów:
Nasze odtwarzanie zakończyło się, klaster jest gotowy do normalnej pracy. Sprawdzamy obecność i wielkość naszej odzyskanej tabelki:
Jeśli potrzebujemy odzyskać tylko wybrane tabele, nie musimy cofać w czasie stanu całego klastra produkcyjnego. Możemy odzyskać do wybranego punktu w czasie klaster "na boku", uruchomić go na innym porcie i przepompować dane z odzyskanego klastra do docelowego za pomocą pg_dump.
Komentarze (0)
Brak komentarzy...