PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Backup incrementale di PostgreSQL e ripristino point-in-time

PostgreSQL offre la possibilità di eseguire backup incrementali e ripristino point-in-time pronto all'uso. Continua a leggere per saperne di più sulle impostazioni e le procedure per raggiungere questo obiettivo.

Inizia con file WAL

WAL sta per Registro Scrivi in ​​anticipo . I WAL sono utilizzati in quasi tutti i moderni sistemi RDBMS per fornire transazioni atomiche e durature.

Le modifiche ai dati contenuti in un cluster di database PostgreSQL gestito da un singolo processo del server PostgreSQL sono possibili solo tramite transazioni. Le modifiche apportate ai dati dalle transazioni sono registrate come una sequenza ordinata di record WAL . Questi record vengono scritti in file di lunghezza fissa chiamati file di segmento WAL o semplicemente file WAL .

I file WAL si trovano in $PGDATA/pg_wal , dove $PGDATA è la directory dei dati per il cluster di database. Su un'installazione Debian predefinita, ad esempio, la directory dei file WAL per il cluster principale è /var/lib/postgresql/10/main/pg_wal . Ecco come appare:

# 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

I file WAL vengono generati in modo incrementale, in sequenza, a partire dalla creazione del cluster. Continuano a essere generati per tutto il tempo in cui si verificano modifiche al cluster. Il meccanismo del file WAL è essenziale per il funzionamento di PostgreSQL e non può essere disattivato.

Dopo che le modifiche sono state scritte per la prima volta come record WAL, devono essere applicate alla rappresentazione su disco dei dati stessi. Questo processo è chiamatocheckpoint , e avviene automaticamente in background (può anche essere forzato manualmente). Il punto fino al quale è stato eseguito il checkpoint è chiamato punto REDO . Anche il checkpoint è una parte essenziale dell'architettura di Postgres e non può essere disattivato.

Conservazione file WAL

Durante il normale funzionamento del server PostgreSQL, i file WAL continueranno a essere scritti nel pg_wal directory. Ma perché averli in giro?

Uno dei motivi è il ripristino in caso di arresto anomalo. Se il server PostgreSQL si arresta in modo anomalo e si riavvia, inizia ad applicare le modifiche dai record WAL ai file di dati (checkpoint) dall'ultimo punto REDO. Ciò garantisce che i file di dati siano coerenti con l'ultima transazione completata.

Un altro motivo è legato alla replica in streaming. La replica in streaming funziona inviando i record WAL in standby server, che li memorizzano localmente ed eseguono checkpoint. Gli standby possono rimanere indietro rispetto al server da cui si stanno replicando (chiamato primario ). Ad esempio, se il primario ha generato 100 record WAL e lo standby ha ricevuto e applicato i primi 80, è necessario che i più recenti20 siano disponibili in modo che lo standby possa ricevere e applicare dal record 81 in poi.

Ma sicuramente i file WAL molto vecchi possono essere cancellati? Sì. PostgreSQL può essere istruito a conservare i file WAL più recenti ed eliminare quelli più vecchi. Ci sono tre opzioni di configurazione rilevanti:

  • wal_keep_segments - imposta il numero minimo di file WAL più recenti da conservare nella directory dei file WAL
  • max_wal_size - specifica la dimensione totale massima dei file WAL nella directory dei file WAL. Se viene superato, quelli più vecchi vengono eliminati. Tuttavia, potrebbero esserci delle ragioni (incluso un valore elevato per wal_keep_segments ) che possono impedire che questa impostazione venga rispettata.
  • min_wal_size - specifica una dimensione totale minima per i file WAL. Finché la dimensione effettiva rimane al di sotto di questo valore, nessun file verrà eliminato.

Nella vita reale non è possibile, o richiesto, memorizzare tutti i file WAL precedenti sotto il pg_wal directory.

Archiviazione file WAL

Il vero valore dei file WAL è che sono un flusso di modifiche che possono essere registrate e riprodotte per ottenere una replica coerente di un cluster PostgreSQL. PostgreSQL fornisce un modo per copiare (o "archiviare") ogni file WAL dopo che è stato creato:il comando_archivio opzione di configurazione.

Questa opzione specifica una stringa di comando della shell che viene richiamata dopo la creazione di ogni file WAL. Ecco alcuni esempi:

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

Ci sono anche altre 2 opzioni, che devono essere impostate:

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

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

Compressione WAL

È possibile comprimere i file WAL prima di copiarli in una posizione di archiviazione sicura a lungo termine. Tuttavia, esiste un'opzione chiamata wal_compression . Attivando questa opzione, PostgreSQL comprimerà i singoli record WAL all'interno dei file WAL. I file WAL stessi avranno le stesse dimensioni (in genere 16 MB), ma conterranno una sequenza di record compressi anziché semplici record.

Archiviazione continua

L'archiviazione WAL è anche chiamata archiviazione continua ed è attivo,backup incrementale .

Prima di avviare questo processo di backup incrementale, è necessario un backup completo. Ciò stabilisce una linea di base su cui i file WAL possono essere ripristinati in modo incrementale. Un backup completo può essere eseguito da:

  • chiudere il processo del server Postgres e copiare la directory dei dati del cluster (conservando le autorizzazioni), oppure
  • utilizzando il pg_basebackup su un server Postgres in esecuzione.

Recupero point-in-time (PITR)

PITR si riferisce alla capacità di PostgreSQL di iniziare dal ripristino di un backup completo, quindi recuperare e applicare progressivamente i file WAL archiviati fino a un timestamp specificato.

Per fare ciò, dobbiamo creare un file chiamato "recovery.conf" nella directory dei dati del cluster ripristinato e avviare un server Postgres per quella directory dei dati. Il file recovery.conf contiene il timestamp di destinazione e si presenta così:

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

Il comando_restore specifica come recuperare un file WAL richiesto da PostgreSQL. È l'inverso di archive_command. Il recovery_target_time specifica l'ora fino a quando avremo bisogno delle modifiche.

Quando un processo del server PostgreSQL si avvia e rileva un recovery.conf file nella directory dei dati, si avvia in una modalità speciale chiamata "modalità di ripristino". In modalità di ripristino, le connessioni client vengono rifiutate. Postgres recupera i file WAL e li applica fino al raggiungimento dell'obiettivo di ripristino (in questo caso, cambia fino al timestamp specificato). Quando l'obiettivo viene raggiunto, il server per impostazione predefinita sospende la riproduzione WAL (sono possibili altre azioni). A questo punto, dovresti esaminare lo stato del ripristino e, se tutto sembra a posto, annullare la pausa per uscire dalla modalità di ripristino e continuare il normale funzionamento.

Mettere tutto insieme

Era tutto un mucchio di teoria e testo, proviamolo per vedere come funziona nella pratica.

Per prima cosa inizializziamo un nuovo cluster:

/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

Creeremo anche una directory che fungerà da posizione di archiviazione sicura. Chiamiamo questo "archivio".

/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

È necessario configurare le impostazioni dell'archivio di cui abbiamo discusso in precedenza, prima di poter avviare il server. Quindi aggiungiamo quanto segue alla fine di clus1/postgres.conf :

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

Il nostro comando di archivio copia semplicemente il file WAL nella directory di archivio creata in precedenza.

Abbiamo anche aggiunto archive_timeout collocamento. Di solito, un file WAL viene creato solo quando ci sono record WAL sufficienti per riempire un file WAL da 16 MB. Ciò significa che per i server con poche scritture, potrebbe essere necessario attendere molto tempo per la creazione di un file WAL. L'impostazione archive_timeout dice a Postgres che deve crea un file WAL ogni tanti secondi, indipendentemente dal fatto che sia pieno o meno.

Qui abbiamo impostato questo a 60 (secondi), ma questo è solo per la demo! In genere non vorresti mai mantenerlo così basso.

Facciamo anche una copia di "clus1". Questo è l'equivalente di un backup completo.

/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

Ora possiamo avviare il cluster:

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

Aggiungiamo alcuni dati.

/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

Si noti che ora sono le 14:05. Controlliamo se il nostro comando di archivio funziona:

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

Sì, abbiamo un unico file di archivio. La nostra ultima modifica è stata alle 14:05, ora aspettiamo qualche minuto e poi apportiamo altre modifiche.

/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

Quindi ora abbiamo aggiunto altre 100 righe, alle 14:16. Fermiamo il server:

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

e controlla di nuovo il nostro archivio:

/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

Sembra buono. Ora proveremo a eseguire un ripristino PITR di clus2 fino al momento 14:10.

Per prima cosa modifichiamo postgres.conf di clus2 e aggiungiamo queste righe alla fine:

port = 6001
archive_mode = off

Per riprodurre i file WAL, dobbiamo mettere il server PostgreSQL per clus2 (che non abbiamo ancora avviato) in modalità di ripristino. Per fare ciò, crea il file chiamato "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'

Questo contiene il restore_command che fa l'opposto del precedentecomando_archivio , ovvero copiando il file richiesto dalla directory di archivio alla directory pg_wal.

Abbiamo anche impostato il recovery_target_time alle 14:10.

Ora iniziamo clus2:

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

Per vedere cosa è successo, esaminiamo il file di registro:

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

Il ripristino è stato rapido (nella vita reale, potrebbero essere necessarie ore o giorni) e il registro indica che è stato interrotto prima di una particolare transazione (che ha un timestamp di> 14:10). Dice anche che il ripristino è in pausa e deve essere proseguito manualmente.

Esaminiamo i dati:

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

Vediamo che ci sono solo 10000 righe. Alle 14:16 ne abbiamo aggiunti altri 100, che non sono comparsi nella tabella.

Sembra buono, quindi riprendiamo:

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

(1 row)

Il file di registro ora segnala che il ripristino è completo e le normali operazioni vengono ripristinate:

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

E abbiamo ripristinato con successo il cluster fino a un tempo specificato!

Ulteriori letture

Ecco alcuni punti di partenza per scoprire di più sull'archiviazione WAL, recoverymode e PITR:

  • Documenti:RecoveryConfiguration
  • Documenti:Archiviazione continua e PITR
  • Capitolo 9 dal libro "Gli interni di PostgreSQL"
  • Strumenti:WAL-E, WAL-G, Barman