PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Przyrostowa kopia zapasowa PostgreSQL i odzyskiwanie do określonego momentu

PostgreSQL jest wyposażony w możliwość wykonywania przyrostowych kopii zapasowych i przywracania do określonego momentu. Czytaj dalej, aby dowiedzieć się więcej o ustawieniach i procedurach, aby to osiągnąć.

Zaczyna się od plików WAL

WAL oznacza Dziennik zapisu z wyprzedzeniem . WAL są używane w prawie wszystkich nowoczesnych systemach RDBMS, aby zapewnić trwałe i atomowe transakcje.

Zmiana danych zawartych w klastrze baz danych PostgreSQL zarządzanym przez pojedynczy proces serwera PostgreSQL jest możliwa tylko poprzez transakcje. Modyfikacje danych dokonywane przez transakcje są rejestrowane jako uporządkowana sekwencja rekordów WAL . Te rekordy są zapisywane w plikach o stałej długości, zwanych plikami segmentowymi WAL lub po prostu pliki WAL .

Pliki WAL są na żywo w $PGDATA/pg_wal , gdzie $PGDATA to katalog danych dla klastra bazy danych. Na przykład w domyślnej instalacji Debiana katalog plików WAL dla głównego klastra to /var/lib/postgresql/10/main/pg_wal . Tutaj to wygląda tak:

# pwd
/var/lib/postgresql/10/main/pg_wal
# ls -l
total 278532
-rw------- 1 postgres postgres 16777216 May  7 08:48 00000001000000000000000B
-rw------- 1 postgres postgres 16777216 May  7 10:08 00000001000000000000000C
-rw------- 1 postgres postgres 16777216 May  7 10:08 00000001000000000000000D
-rw------- 1 postgres postgres 16777216 May  7 10:08 00000001000000000000000E
-rw------- 1 postgres postgres 16777216 May  7 10:08 00000001000000000000000F
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000010
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000011
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000012
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000013
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000014
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000015
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000016
-rw------- 1 postgres postgres 16777216 May  7 10:08 000000010000000000000017
-rw------- 1 postgres postgres 16777216 May 16 20:52 000000010000000000000018
-rw------- 1 postgres postgres 16777216 May 16 20:56 000000010000000000000019
-rw------- 1 postgres postgres 16777216 May 26 08:52 00000001000000000000001A
-rw------- 1 postgres postgres 16777216 Jun  2 09:59 00000001000000000000001B
drwx------ 2 postgres postgres     4096 Mar 30 10:06 archive_status

Pliki WAL są generowane przyrostowo, w kolejności, począwszy od tworzenia klastra. Są one generowane tak długo, jak w klastrze następują modyfikacje. Mechanizm plików WAL jest niezbędny do działania PostgreSQL i nie można go wyłączyć.

Po pierwszym zapisaniu zmian jako rekordów WAL należy je zastosować do reprezentacji samych danych na dysku. Ten proces nazywa siępunktami kontrolnymi , i dzieje się w tle automatycznie (można to również wymusić ręcznie). Punkt, do którego wykonano punkt kontrolny, nazywa się punktem REDO . Punkty kontrolne są również istotną częścią architektury Postgres i nie można ich wyłączyć.

Przechowywanie plików WAL

W normalnym toku działania serwera PostgreSQL pliki WAL będą zapisywane w pg_wal informator. Ale po co je mieć?

Jednym z powodów jest odzyskiwanie po awarii. Jeśli serwer PostgreSQL ulegnie awarii i zrestartuje się, zacznie wprowadzać zmiany z rekordów WAL do plików danych (punkty kontrolne) od ostatniego punktu REDO. Gwarantuje to, że pliki danych są zgodne z ostatnią zakończoną transakcją.

Inny powód jest związany z replikacją strumieniową. Replikacja strumieniowa działa poprzez wysyłanie rekordów WAL do gotowości serwery, które przechowują te lokalnie i wykonują punkty kontrolne. Tryby gotowości mogą pozostawać w tyle za serwerem, z którego są replikowane (nazywanym podstawowym ). Na przykład, jeśli podstawowy wygenerował 100 WALrecords, a stan gotowości odebrał i zastosował pierwsze 80, najnowsze 20 muszą być dostępne, aby stan gotowości mógł odbierać i stosować od 81 rekordu.

Ale z pewnością bardzo stare pliki WAL można usunąć? Tak. PostgreSQL może zostać poinstruowany, aby zachowywał najnowsze pliki WAL i usuwał starsze. Istnieją trzy odpowiednie opcje konfiguracji:

  • wal_keep_segments - ustawia minimalną liczbę najnowszych plików WAL, które mają być przechowywane w katalogu plików WAL
  • max_wal_size — określa maksymalny łączny rozmiar plików WAL w katalogu plików WAL. Jeśli ta zostanie przekroczona, starsze są usuwane. Jednak mogą istnieć powody (w tym wysoka wartość wal_keep_segments ), które mogą uniemożliwić honorowanie tego ustawienia.
  • min_wal_size — określa minimalny całkowity rozmiar plików WAL. Dopóki rzeczywisty rozmiar pozostanie poniżej tej wartości, żadne pliki nie zostaną usunięte.

W rzeczywistości nie jest możliwe ani nie jest wymagane przechowywanie wszystkich poprzednich plików WAL w pg_wal katalog.

Archiwizacja plików WAL

Prawdziwą wartością plików WAL jest to, że są one strumieniem zmian, które można rejestrować i odtwarzać w celu uzyskania spójnej repliki klastra PostgreSQL. PostgreSQL zapewnia sposób, w jaki możemy skopiować (lub „zarchiwizować”) każdy plik WAL po jego otrzymaniu utworzony – archive_command opcja konfiguracji.

Ta opcja określa ciąg poleceń powłoki, który jest wywoływany po utworzeniu każdego pliku WAL. Oto kilka przykładów:

# Copy the file to a safe location (like a mounted NFS volume)
archive_command = 'cp %p /mnt/nfs/%f'

# Not overwriting files is a good practice
archive_command = 'test ! -f /mnt/nfs/%f && cp %p /mnt/nfs/%f'

# Copy to S3 bucket
archive_command = 's3cmd put %p s3://BUCKET/path/%f'

# Copy to Google Cloud bucket
archive_command = 'gsutil cp %p gs://BUCKET/path/%f'

# An external script
archive_command = '/opt/scripts/archive_wal %p'

Istnieją również 2 inne opcje, które należy ustawić:

# this must be "on" to enable WAL archiving
archive_mode = on

# has to be "replica" (default) or "logical" for WAL archiving
wal_level = replica

Kompresja WAL

Możesz skompresować pliki WAL przed skopiowaniem ich do długoterminowej/bezpiecznej lokalizacji. Istnieje jednak opcja o nazwie wal_compression . Włączenie tej opcji spowoduje, że PostgreSQL skompresuje poszczególne rekordy WAL w plikach WAL. Same pliki WAL będą miały ten sam rozmiar (zwykle 16 MB), ale będą zawierać sekwencję skompresowanych rekordów, a nie zwykłych rekordów.

Ciągła archiwizacja

Archiwizacja WAL jest również nazywana archiwizacją ciągłą i działa,przyrostowa kopia zapasowa .

Przed rozpoczęciem procesu tworzenia przyrostowej kopii zapasowej wymagana jest pełna kopia zapasowa.Ustanawia to punkt odniesienia, na którym można przywracać przyrostowo pliki WAL.Pełną kopię zapasową można wykonać:

  • zamykanie procesu serwera Postgres i kopiowanie katalogu danych klastra (przy zachowaniu uprawnień) lub
  • za pomocą pg_basebackup na uruchomionym serwerze Postgres.

Odzyskiwanie punktu w czasie (PITR)

PITR odnosi się do zdolności PostgreSQL do rozpoczęcia od przywrócenia pełnej kopii zapasowej, a następnie stopniowego pobierania i stosowania zarchiwizowanych plików WAL do określonej sygnatury czasowej.

Aby to zrobić, musimy utworzyć plik o nazwie „recovery.conf” w katalogu danych restorecluster i uruchomić serwer Postgres dla tego katalogu danych. Plik recovery.conf zawiera docelowy znacznik czasu i wygląda tak:

restore_command = 'cp /tmp/demo/archive/%f "%p"'
recovery_target_time = '2019-06-04 14:10:00'

restore_command określa, jak pobrać plik WAL wymagany przez PostgreSQL. Jest to odwrotność polecenia archive_command. czas_odzyskiwania_docelowego określa czas, do którego będziemy potrzebować zmian.

Gdy proces serwera PostgreSQL zostanie uruchomiony i wykryje plik recovery.conf pliku w katalogu danych, uruchamia się w specjalnym trybie zwanym „trybem odzyskiwania”. W trybie odzyskiwania połączenia klientów są odrzucane. Postgres pobiera pliki WAL i stosuje je do momentu osiągnięcia celu odzyskiwania (w tym przypadku zmiany do określonej sygnatury czasowej). Gdy cel zostanie osiągnięty, serwer domyślnie wstrzymuje odtwarzanie WAL (możliwe są inne działania). W tym momencie powinieneś sprawdzić stan przywracania i jeśli wszystko wygląda dobrze, cofnij pauzę, aby wyjść z trybu przywracania i kontynuować normalne działanie.

Łączenie wszystkiego w całość

Wszystko to było całą masą teorii i tekstu, wypróbujmy to, aby zobaczyć, jak to wszystko działa w praktyce.

Najpierw zainicjujmy nowy klaster:

/tmp/demo$ pg_ctl -D clus1 initdb
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "C.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

creating directory clus1 ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    /usr/lib/postgresql/10/bin/pg_ctl -D clus1 -l logfile start

Stworzymy również katalog, który będzie służył jako nasze bezpieczne miejsce przechowywania.Nazwijmy to „archiwum”.

/tmp/demo$ mkdir archive
/tmp/demo$ ls -l
total 8
drwxr-xr-x  2 postgres postgres 4096 Jun  4 14:02 archive
drwx------ 19 postgres postgres 4096 Jun  4 14:02 clus1

Musimy skonfigurować ustawienia archiwum, o których mówiliśmy wcześniej, zanim będziemy mogli uruchomić serwer. Dodajmy więc następujące na końcu clus1/postgres.conf :

port = 6000
wal_level = logical
archive_mode = on
archive_command = 'cp %p /tmp/demo/archive/%f'
archive_timeout = 60

Nasze polecenie archiwum po prostu kopiuje plik WAL do utworzonego wcześniej katalogu archiwum.

Dodaliśmy również archive_timeout ustawienie. Zwykle plik WAL jest tworzony tylko wtedy, gdy liczba rekordów WAL jest wystarczająca do wypełnienia pliku WAL o wielkości 16 MB. Oznacza to, że w przypadku serwerów z niewielką liczbą zapisów może być konieczne długie oczekiwanie na utworzenie pliku WAL. Ustawienie archive_timeout mówi Postgresowi, że musi twórz plik aWAL co tyle sekund, niezależnie od tego, czy jest pełny, czy nie.

Tutaj ustawiliśmy to na 60 (sekund), ale to tylko dla wersji demo! Zazwyczaj nigdy nie chcesz utrzymywać go tak nisko.

Zróbmy też kopię „clus1”. Jest to odpowiednik pełnej kopii zapasowej.

/tmp/demo$ cp -Rp clus1 clus2
/tmp/demo$ ls -l
total 12
drwxr-xr-x  2 postgres postgres 4096 Jun  4 14:02 archive
drwx------ 19 postgres postgres 4096 Jun  4 14:03 clus1
drwx------ 19 postgres postgres 4096 Jun  4 14:03 clus2

Teraz możemy uruchomić klaster:

/tmp/demo$ pg_ctl -D clus1 -l log1 start
waiting for server to start.... done
server started

Dodajmy trochę danych.

/tmp/demo$ psql -h /var/run/postgresql -p 6000 postgres
psql (10.8 (Ubuntu 10.8-0ubuntu0.18.04.1))
Type "help" for help.

postgres=# create database demo;
CREATE DATABASE
postgres=# \c demo
You are now connected to database "demo" as user "postgres".
demo=# create table tbl1 (col1 int);
CREATE TABLE
demo=# insert into tbl1 (col1) select generate_series(1, 10000);
INSERT 0 10000
demo=# select count(*) from tbl1;
 count
-------
 10000
(1 row)

demo=# select now();
              now
-------------------------------
 2019-06-04 14:05:05.657871+00
(1 row)

demo=# \q

Zauważ, że teraz jest godzina 14:05. Sprawdźmy, czy nasze polecenie archiwum działa:

/tmp/demo$ ls -l archive/
total 16384
-rw------- 1 postgres postgres 16777216 Jun  4 14:04 000000010000000000000001

Tak, mamy jeden plik archiwum. Nasza ostatnia zmiana miała miejsce o 14:05, poczekajmy teraz kilka minut, a następnie dokonajmy kilku zmian.

/tmp/demo$ psql -h /var/run/postgresql -p 6000 demo
psql (10.8 (Ubuntu 10.8-0ubuntu0.18.04.1))
Type "help" for help.

demo=# select now();
              now
-------------------------------
 2019-06-04 14:16:06.093859+00
(1 row)

demo=# select count(*) from tbl1;
 count
-------
 10000
(1 row)

demo=# insert into tbl1 (col1) select generate_series(1, 100);
INSERT 0 100
demo=# select count(*) from tbl1;
 count
-------
 10100
(1 row)

demo=# \q

Więc teraz dodaliśmy 100 kolejnych rzędów o 14:16. Zatrzymajmy serwer:

/tmp/demo$ pg_ctl -D clus1 stop
waiting for server to shut down.... done
server stopped
/tmp/demo$

i ponownie sprawdź nasze archiwum:

/tmp/demo$ ls -l archive/
total 65536
-rw------- 1 postgres postgres 16777216 Jun  4 14:04 000000010000000000000001
-rw------- 1 postgres postgres 16777216 Jun  4 14:05 000000010000000000000002
-rw------- 1 postgres postgres 16777216 Jun  4 14:09 000000010000000000000003
-rw------- 1 postgres postgres 16777216 Jun  4 14:16 000000010000000000000004

Wygląda dobrze. Teraz spróbujemy odzyskać PITR clus2 do godziny 14:10.

Najpierw edytujmy postgres.conf clus2 i dodajmy te wiersze na końcu:

port = 6001
archive_mode = off

Aby odtworzyć pliki WAL, musimy przełączyć serwer PostgreSQL dla clus2 (którego jeszcze nie uruchomiliśmy) w tryb odzyskiwania. Aby to zrobić, utwórz plik o nazwie „recovery.conf” w clus2:

/tmp/demo$ cat clus2/recovery.conf
restore_command = 'cp /tmp/demo/archive/%f "%p"'
recovery_target_time = '2019-06-04 14:10:00'

Zawiera restore_command co jest przeciwieństwem wcześniejszegoarchive_command , a mianowicie skopiowanie żądanego pliku z katalogu archiwum do katalogu pg_wal.

Ustawiliśmy również recovery_target_time do 14:10.

Teraz zaczynamy clus2:

/tmp/demo$ pg_ctl -D clus2 -l log2 start
waiting for server to start.... done
server started

Aby zobaczyć, co się stało, przyjrzyjmy się plikowi dziennika:

/tmp/demo$ cat log2
2019-06-04 14:19:10.862 UTC [10513] LOG:  listening on IPv4 address "127.0.0.1", port 6001
2019-06-04 14:19:10.864 UTC [10513] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.6001"
2019-06-04 14:19:10.883 UTC [10514] LOG:  database system was shut down at 2019-06-04 14:02:31 UTC
2019-06-04 14:19:10.883 UTC [10514] LOG:  starting point-in-time recovery to 2019-06-04 14:10:00+00
2019-06-04 14:19:10.903 UTC [10514] LOG:  restored log file "000000010000000000000001" from archive
2019-06-04 14:19:10.930 UTC [10514] LOG:  consistent recovery state reached at 0/16383E8
2019-06-04 14:19:10.930 UTC [10514] LOG:  redo starts at 0/16383E8
2019-06-04 14:19:10.931 UTC [10513] LOG:  database system is ready to accept read only connections
2019-06-04 14:19:11.037 UTC [10514] LOG:  restored log file "000000010000000000000002" from archive
2019-06-04 14:19:11.079 UTC [10514] LOG:  restored log file "000000010000000000000003" from archive
2019-06-04 14:19:11.122 UTC [10514] LOG:  restored log file "000000010000000000000004" from archive
2019-06-04 14:19:11.141 UTC [10514] LOG:  recovery stopping before commit of transaction 559, time 2019-06-04 14:16:24.875517+00
2019-06-04 14:19:11.141 UTC [10514] LOG:  recovery has paused
2019-06-04 14:19:11.141 UTC [10514] HINT:  Execute pg_wal_replay_resume() to continue.

Odzyskiwanie było szybkie (w prawdziwym życiu może to zająć godziny lub dni), a stany dziennika zostały zatrzymane przed konkretną transakcją (o sygnaturze czasowej> 14:10). Mówi również, że odzyskiwanie jest wstrzymane i musi być kontynuowane ręcznie.

Zbadajmy dane:

/tmp/demo$ psql -h /var/run/postgresql -p 6001 demo
psql (10.8 (Ubuntu 10.8-0ubuntu0.18.04.1))
Type "help" for help.

demo=# select count(*) from tbl1;
 count
-------
 10000
(1 row)

Widzimy, że jest tylko 10000 wierszy. O 14:16 dodaliśmy jeszcze 100, które nie pojawiły się w tabeli.

To wygląda dobrze, więc powróćmy:

demo=# select pg_wal_replay_resume();
 pg_wal_replay_resume
----------------------

(1 row)

Plik dziennika informuje teraz o zakończeniu odzyskiwania i przywróceniu normalnych operacji:

2019-06-04 14:20:26.219 UTC [10514] LOG:  redo done at 0/4002160
2019-06-04 14:20:26.219 UTC [10514] LOG:  last completed transaction was at log time 2019-06-04 14:05:28.813325+00
cp: cannot stat '/tmp/demo/archive/00000002.history': No such file or directory
2019-06-04 14:20:26.228 UTC [10514] LOG:  selected new timeline ID: 2
2019-06-04 14:20:26.272 UTC [10514] LOG:  archive recovery complete
cp: cannot stat '/tmp/demo/archive/00000001.history': No such file or directory
2019-06-04 14:20:26.388 UTC [10513] LOG:  database system is ready to accept connections

I udało nam się odzyskać klaster do określonego czasu!

Dalsze czytanie

Oto kilka punktów wyjścia, aby dowiedzieć się więcej o archiwizacji WAL, trybie odzyskiwania i PITR:

  • Dokumenty:konfiguracja odzyskiwania
  • Dokumenty:ciągła archiwizacja i PITR
  • Rozdział 9 z książki „Wewnętrzne elementy PostgreSQL”
  • Narzędzia:WAL-E,WAL-G, Barman

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Zmienne zdefiniowane przez użytkownika w PostgreSQL

  2. Najlepszy sposób na liczenie rekordów w dowolnych odstępach czasu w Rails+Postgres

  3. Jak używać struktury danych pierścienia w funkcjach okien

  4. Czy można podać parametry dla nazwy tabeli lub kolumny w przygotowanych instrukcjach lub w QueryRunner.update()?

  5. jak wyświetlić pełny kod procedury składowanej?