PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Inkrementelle PostgreSQL-Sicherung und Point-in-Time-Wiederherstellung

PostgreSQL bietet die Möglichkeit, inkrementelle Backups und Point-in-Time-Recovery out of the box durchzuführen. Lesen Sie weiter, um mehr über die Einstellungen und Verfahren zu erfahren, um dies zu erreichen.

Es beginnt mit WAL-Dateien

WAL steht für Write Ahead Log . WALs werden in fast allen modernen RDBMS-Systemen verwendet, um dauerhafte und atomare Transaktionen bereitzustellen.

Änderungen an den Daten, die in einem PostgreSQL-Datenbankcluster enthalten sind, der von einem einzelnen PostgreSQL-Serverprozess verwaltet wird, sind nur über Transaktionen möglich. Die durch Transaktionen an den Daten vorgenommenen Änderungen werden als geordnete Folge von WAL-Datensätzen aufgezeichnet . Diese Aufzeichnungen werden in Dateien mit fester Länge geschrieben, die als WAL-Segmentdateien bezeichnet werden , oder einfach WAL-Dateien .

WAL-Dateien befinden sich in $PGDATA/pg_wal , wobei $PGDATA ist das Datenverzeichnis für den Datenbank-Cluster. Bei einer standardmäßigen Debian-Installation ist das WAL-Dateiverzeichnis für den Hauptcluster beispielsweise /var/lib/postgresql/10/main/pg_wal . So sieht es aus:

# 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

WAL-Dateien werden inkrementell nacheinander generiert, beginnend mit der Clustererstellung. Sie werden so lange generiert, wie Änderungen am Cluster vorgenommen werden. Der WAL-Dateimechanismus ist für das Funktionieren von PostgreSQL unerlässlich und kann nicht deaktiviert werden.

Nachdem die Änderungen zunächst als WAL-Einträge ausgeschrieben wurden, müssen sie auf die Darstellung der Daten selbst auf der Platte angewendet werden. Dieser Vorgang wird Checkpointing genannt , und geschieht automatisch im Hintergrund (es kann auch manuell erzwungen werden). Der Punkt, bis zu dem das Checkpointing durchgeführt wurde, wird als REDO-Punkt bezeichnet . Checkpointing ist auch ein wesentlicher Bestandteil der Postgres-Architektur und kann nicht deaktiviert werden.

Aufbewahrung von WAL-Dateien

Im normalen Verlauf des PostgreSQL-Serverbetriebs werden WAL-Dateien weiterhin in pg_wal geschrieben Verzeichnis. Aber warum haben sie sie in der Nähe?

Ein Grund ist die Wiederherstellung nach einem Absturz. Wenn der PostgreSQL-Server abstürzt und neu startet, beginnt er, seit dem letzten REDO-Punkt Änderungen von WAL-Einträgen in die Datendateien (Checkpointing) anzuwenden. Dies garantiert, dass die Datendateien mit der letzten abgeschlossenen Transaktion konsistent sind.

Ein weiterer Grund hängt mit der Streaming-Replikation zusammen. Die Streaming-Replikation funktioniert, indem WAL-Einträge an Standby gesendet werden Server, die diese lokal speichern und Checkpoints durchführen. Standbys können hinter dem Server zurückbleiben, von dem sie replizieren (sogenannter primärer Server). ). Wenn beispielsweise der Primäre 100 WAL-Datensätze generiert hat und der Standby die ersten 80 empfangen und angewendet hat, müssen die neuesten 20 verfügbar sein, damit der Standby ab Datensatz 81 empfangen und anwenden kann.

Aber sicher können die sehr alten WAL-Dateien gelöscht werden? Ja. PostgreSQL kann angewiesen werden, die neuesten WAL-Dateien beizubehalten und die älteren zu löschen. Es gibt drei relevante Konfigurationsoptionen:

  • wal_keep_segments - legt die Mindestanzahl der neuesten WAL-Dateien fest, die im WAL-Dateiverzeichnis aufbewahrt werden
  • max_wal_size - gibt die maximale Gesamtgröße von WAL-Dateien im WAL-Dateiverzeichnis an. Wird diese überschritten, werden ältere gelöscht. Es kann jedoch Gründe geben (einschließlich eines hohen Werts für wal_keep_segments ), die verhindern können, dass diese Einstellung beachtet wird.
  • min_wal_size - gibt eine minimale Gesamtgröße für WAL-Dateien an. Solange die tatsächliche Größe unter diesem Wert bleibt, werden keine Dateien gelöscht.

Im wirklichen Leben ist es nicht möglich oder erforderlich, alle vorherigen WAL-Dateien unter pg_wal zu speichern Verzeichnis.

WAL-Dateiarchivierung

Der wahre Wert von WAL-Dateien besteht darin, dass es sich um einen Strom von Änderungen handelt, die aufgezeichnet und wiedergegeben werden können, um eine konsistente Kopie eines PostgreSQL-Clusters zu erhalten erstellt – der archive_command Konfigurationsoption.

Diese Option gibt eine Shell-Befehlszeichenfolge an, die aufgerufen wird, nachdem jede WAL-Datei erstellt wurde. Hier sind einige Beispiele:

# 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'

Es gibt noch 2 weitere Optionen, die gesetzt werden müssen:

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

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

WAL-Komprimierung

Sie können die WAL-Dateien komprimieren, bevor Sie sie an einen langfristigen/sicheren Speicherort kopieren. Es gibt jedoch eine Option namens wal_compression . Wenn Sie diese Option aktivieren, komprimiert PostgreSQL die einzelnen WAL-Datensätze in den WAL-Dateien. Die WAL-Dateien selbst haben die gleiche Größe (normalerweise 16 MB), enthalten jedoch eine Folge komprimierter Datensätze und keine einfachen Datensätze.

Kontinuierliche Archivierung

Die WAL-Archivierung wird auch als kontinuierliche Archivierung bezeichnet und ist in Kraft,inkrementelle Sicherung .

Bevor Sie mit diesem Prozess der inkrementellen Sicherung beginnen, ist eine vollständige Sicherung erforderlich. Dadurch wird eine Basislinie festgelegt, auf der WAL-Dateien inkrementell wiederhergestellt werden können. Eine vollständige Sicherung kann folgendermaßen erstellt werden:

  • Herunterfahren des Postgres-Serverprozesses und Kopieren des Cluster-Datenverzeichnisses (unter Beibehaltung der Berechtigungen) oder
  • unter Verwendung von pg_basebackup auf einem laufenden Postgres-Server.

Point-in-time-recovery (PITR)

PITR bezieht sich auf die Fähigkeit von PostgreSQL, mit der Wiederherstellung einer vollständigen Sicherung zu beginnen und dann schrittweise archivierte WAL-Dateien bis zu einem bestimmten Zeitstempel abzurufen und anzuwenden.

Dazu müssen wir im Datenverzeichnis des wiederhergestellten Clusters eine Datei namens „recovery.conf“ erstellen und einen Postgres-Server für dieses Datenverzeichnis starten. Die Datei recovery.conf enthält den Zielzeitstempel und sieht folgendermaßen aus:

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

Der restore_command gibt an, wie eine von PostgreSQL benötigte WAL-Datei abgerufen wird. Sie ist die Umkehrung von archive_command. Die recovery_target_time gibt die Zeit an, bis wann wir die Änderungen benötigen.

Wenn ein PostgreSQL-Serverprozess startet und eine recovery.conf entdeckt Datei im Datenverzeichnis, startet es in einem speziellen Modus namens „Wiederherstellungsmodus“. Im Wiederherstellungsmodus werden Client-Verbindungen abgelehnt. Postgres ruft WAL-Dateien ab und wendet sie an, bis das Wiederherstellungsziel (in diesem Fall Änderungen bis zum angegebenen Zeitstempel) erreicht ist. Wenn das Ziel erreicht ist, pausiert der Server standardmäßig die WAL-Wiedergabe (andere Aktionen sind möglich). An diesem Punkt sollten Sie den Status der Wiederherstellung überprüfen und, wenn alles in Ordnung aussieht, die Pause fortsetzen, um den Wiederherstellungsmodus zu beenden und den normalen Betrieb fortzusetzen.

Alles zusammenfügen

Das war alles eine ganze Menge Theorie und Text, probieren wir es aus, um zu sehen, wie es in der Praxis funktioniert.

Lassen Sie uns zuerst einen neuen Cluster initialisieren:

/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

Wir erstellen auch ein Verzeichnis, das als unser sicherer Speicherort dient. Nennen wir es „Archiv“.

/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

Wir müssen die zuvor besprochenen Archiveinstellungen konfigurieren, bevor wir den Server starten können. Fügen wir also Folgendes am Ende von clus1/postgres.conf hinzu :

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

Unser Archivierungsbefehl kopiert einfach die WAL-Datei in das Archivverzeichnis, das wir zuvor erstellt haben.

Wir haben auch das archive_timeout hinzugefügt Einstellung. Normalerweise wird eine WAL-Datei nur dann erstellt, wenn genügend WAL-Einträge vorhanden sind, um eine 16-MB-WAL-Datei zu füllen. Dies bedeutet, dass Sie bei Servern mit wenigen Schreibvorgängen möglicherweise lange warten müssen, bis eine WAL-Datei erstellt wird. Die Einstellung archive_timeout teilt Postgres mit, dass es muss erstellt alle paar Sekunden eine WAL-Datei, unabhängig davon, ob sie voll ist oder nicht.

Hier haben wir dies auf 60 (Sekunden) eingestellt, aber das ist nur für die Demo! Normalerweise möchten Sie es nie so niedrig halten.

Lassen Sie uns auch eine Kopie von „clus1“ erstellen. Dies entspricht einer vollständigen Sicherung.

/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

Jetzt können wir den Cluster starten:

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

Lassen Sie uns einige Daten hinzufügen.

/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

Beachten Sie, dass es jetzt 14:05 Uhr ist. Lassen Sie uns überprüfen, ob unser Archivierungsbefehl funktioniert:

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

Ja, wir haben eine einzige Archivdatei. Unsere letzte Änderung war um 14:05 Uhr, warten wir jetzt ein paar Minuten und nehmen dann weitere Änderungen vor.

/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

Jetzt haben wir um 14:16 Uhr 100 weitere Zeilen hinzugefügt. Stoppen wir den Server:

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

und überprüfen Sie erneut unser Archiv:

/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

Sieht gut aus. Jetzt versuchen wir, eine PITR-Wiederherstellung von clus2 bis zum Zeitpunkt 14:10 durchzuführen.

Lassen Sie uns zuerst die postgres.conf von clus2 bearbeiten und diese Zeilen am Ende hinzufügen:

port = 6001
archive_mode = off

Um die WAL-Dateien wiederzugeben, müssen wir den PostgreSQL-Server für clus2 (den wir noch nicht gestartet haben) in den Wiederherstellungsmodus versetzen. Erstellen Sie dazu die Datei „recovery.conf“ in clus2:

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

Diese enthält den restore_command was das Gegenteil des früheren archive_command macht , nämlich Kopieren der angeforderten Datei aus dem Archivverzeichnis in das pg_wal-Verzeichnis.

Wir haben auch die recovery_target_time festgelegt bis 14:10.

Jetzt starten wir clus2:

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

Um zu sehen, was passiert ist, sehen wir uns die Protokolldatei an:

/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.

Die Wiederherstellung war schnell (im wirklichen Leben kann es Stunden oder Tage dauern) und das Protokoll gibt an, dass sie vor einer bestimmten Transaktion (die einen Zeitstempel von> 14:10 hat) gestoppt wurde. Außerdem heißt es, dass die Wiederherstellung angehalten wurde und manuell fortgesetzt werden muss.

Untersuchen wir die Daten:

/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)

Wir sehen, dass es nur 10000 Zeilen gibt. Um 14:16 Uhr haben wir 100 weitere hinzugefügt, die noch nicht in der Tabelle erschienen sind.

Das sieht gut aus, also machen wir weiter:

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

(1 row)

Die Protokolldatei meldet nun, dass die Wiederherstellung abgeschlossen ist und der normale Betrieb wiederhergestellt wird:

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

Und wir haben den Cluster bis zu einem bestimmten Zeitpunkt erfolgreich wiederhergestellt!

Weiterführende Literatur

Hier sind einige Ausgangspunkte, um mehr über die WAL-Archivierung, den Wiederherstellungsmodus und PITR zu erfahren:

  • Docs:Wiederherstellungskonfiguration
  • Docs:Kontinuierliche Archivierung und PITR
  • Kapitel 9 aus dem Buch „The Internals of PostgreSQL“
  • Werkzeuge:WAL-E, WAL-G, Barmann