sql >> Base de Datos >  >> RDS >> PostgreSQL

Copia de seguridad incremental de PostgreSQL y recuperación de un punto en el tiempo

PostgreSQL viene con la capacidad de realizar copias de seguridad incrementales y recuperaciones puntuales listas para usar. Siga leyendo para obtener más información sobre la configuración y los procedimientos para lograrlo.

Comienza con archivos WAL

WAL significa Registro de escritura anticipada . Los WAL se utilizan en casi todos los sistemas RDBMS modernos para proporcionar transacciones atómicas y duraderas.

Los cambios en los datos contenidos en un clúster de base de datos PostgreSQL administrado por un único proceso de servidor PostgreSQL solo son posibles mediante transacciones. Las modificaciones realizadas a los datos por transacciones se registran como una secuencia ordenada de registros WAL . Estos registros se escriben en archivos de longitud fija llamados archivos de segmento WAL , o simplemente archivos WAL .

Los archivos WAL viven en $PGDATA/pg_wal , donde $PGDATA es el directorio de datos para el clúster de la base de datos. En una instalación predeterminada de Debian, por ejemplo, el directorio de archivos WAL para el clúster principal es /var/lib/postgresql/10/main/pg_wal . Así es como se ve:

# 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

Los archivos WAL se generan de forma incremental, en secuencia, a partir de la creación del clúster. Siguen generándose mientras se produzcan modificaciones en el clúster. El mecanismo de archivo WAL es esencial para el funcionamiento de PostgreSQL y no se puede desactivar.

Una vez que los cambios se escriben por primera vez como registros WAL, deben aplicarse a la representación en disco de los datos mismos. Este proceso se llama puntos de control , y sucede en segundo plano automáticamente (también se puede forzar manualmente). El punto hasta el que se realizó el control se denomina punto REDO . Los puntos de control también son una parte esencial de la arquitectura de Postgres y no se pueden desactivar.

Retención de archivos WAL

En el curso normal de la operación del servidor PostgreSQL, los archivos WAL se seguirán escribiendo en el pg_wal directorio. Pero, ¿por qué tenerlos cerca?

Una razón es la recuperación de fallas. Si el servidor PostgreSQL falla y se reinicia, comienza a aplicar los cambios de los registros WAL en los archivos de datos (puntos de control) desde el último punto REDO. Esto garantiza que los archivos de datos sean consistentes con la última transacción completada.

Otra razón está relacionada con la replicación de transmisión. La replicación de transmisión funciona mediante el envío de registros WAL a espera servidores, que los almacenan localmente y realizan puntos de control. Los servidores de reserva pueden retrasarse con respecto al servidor desde el que se replican (llamado servidor principal). ). Por ejemplo, si el primario ha generado 100 registros WAL y el standby ha recibido y aplicado los primeros 80, se requiere que los 20 más recientes estén disponibles para que el standby pueda recibir y aplicar desde el registro 81 en adelante.

¿Pero seguramente los archivos WAL muy antiguos se pueden eliminar? Sí. Se puede indicar a PostgreSQL que retenga los archivos WAL más recientes y elimine los más antiguos. Hay tres opciones de configuración relevantes:

  • segmentos_de_mantenimiento_de_wal - establece el número mínimo de archivos WAL más recientes que se conservarán en el directorio de archivos WAL
  • max_wal_size - especifica el tamaño total máximo de los archivos WAL en el directorio de archivos WAL. Si se supera, se eliminan los más antiguos. Sin embargo, puede haber razones (incluido un valor alto para wal_keep_segments ) que puede evitar que se respete esta configuración.
  • min_wal_size - especifica un tamaño total mínimo para los archivos WAL. Mientras el tamaño real se mantenga por debajo de este valor, no se eliminará ningún archivo.

En la vida real, no es posible ni necesario almacenar todos los archivos WAL anteriores bajo el pg_wal directorio.

Archivo de archivos WAL

El valor real de los archivos WAL es que son un flujo de cambios que se pueden grabar y reproducir para obtener una réplica coherente de un clúster de PostgreSQL. PostgreSQL proporciona una forma en la que podemos copiar (o "archivar") cada archivo WAL después de obtener creado:el archive_command opción de configuración.

Esta opción especifica una cadena de comando de shell que se invoca después de crear cada archivo WAL. Estos son algunos ejemplos:

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

También hay otras 2 opciones que deben configurarse:

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

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

Compresión WAL

Puede comprimir los archivos WAL antes de copiarlos en una ubicación de almacenamiento seguro/a largo plazo. Sin embargo, hay una opción llamada wal_compression . Activar esto hará que PostgreSQL comprima los registros WAL individuales dentro de los archivos WAL. Los archivos WAL en sí tendrán el mismo tamaño (normalmente 16 MB), pero contendrán una secuencia de registros comprimidos en lugar de registros sin formato.

Archivo continuo

El archivo WAL también se denomina archivo continuo y está en vigor, copia de seguridad incremental .

Antes de iniciar este proceso de copia de seguridad incremental, se requiere una copia de seguridad completa. Esto establece una línea de base sobre la cual los archivos WAL se pueden restaurar de forma incremental. Se puede realizar una copia de seguridad completa mediante:

  • cerrando el proceso del servidor de Postgres y copiando el directorio de datos del clúster (manteniendo los permisos), o
  • utilizando pg_basebackup en un servidor Postgres en ejecución.

Recuperación de un punto en el tiempo (PITR)

PITR se refiere a la capacidad de PostgreSQL para comenzar desde la restauración de una copia de seguridad completa, luego recuperar y aplicar progresivamente archivos WAL archivados hasta una marca de tiempo específica.

Para hacer esto, debemos crear un archivo llamado "recovery.conf" en el directorio de datos del clúster restaurado e iniciar un servidor Postgres para ese directorio de datos. El archivo recovery.conf contiene la marca de tiempo de destino y se ve así:

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

El comando_restaurar especifica cómo obtener un archivo WAL requerido por PostgreSQL. Es lo contrario de archive_command. El recovery_target_time especifica el tiempo hasta cuando necesitamos los cambios.

Cuando un proceso de servidor PostgreSQL se inicia y descubre un recovery.conf archivo en el directorio de datos, se inicia en un modo especial llamado "modo de recuperación". Cuando está en modo de recuperación, se rechazan las conexiones del cliente. Postgres obtiene los archivos WAL y los aplica hasta que se alcanza el objetivo de recuperación (en este caso, los cambios hasta la marca de tiempo especificada). Cuando se alcanza el objetivo, el servidor detiene de forma predeterminada la reproducción de WAL (son posibles otras acciones). En este punto, se supone que debe examinar el estado de la restauración y, si todo se ve bien, cancelar la pausa para salir del modo de recuperación y continuar con la operación normal.

Unir todo

Todo eso fue un montón de teoría y texto, probemos para ver cómo funciona todo en la práctica.

Primero, inicialicemos un nuevo clúster:

/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

También crearemos un directorio que servirá como nuestra ubicación de almacenamiento segura. Llamemos a esto "archivo".

/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

Necesitamos configurar los ajustes de archivo que discutimos anteriormente, antes de que podamos iniciar el servidor. Así que agreguemos lo siguiente al final de clus1/postgres.conf :

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

Nuestro comando de archivo simplemente copia el archivo WAL en el directorio de archivo que creamos anteriormente.

También hemos agregado el archive_timeout entorno. Por lo general, un archivo WAL se crea solo cuando hay suficientes registros WAL para llenar un archivo WAL de 16 MB. Esto significa que para servidores con pocas escrituras, es posible que deba esperar mucho tiempo para que se cree un archivo WAL. La configuración archive_timeout le dice a Postgres que debe crea un archivo WAL cada tantos segundos, independientemente de si está lleno o no.

Aquí hemos establecido esto en 60 (segundos), ¡pero esto es solo para la demostración! Por lo general, nunca querrás mantenerlo tan bajo.

Hagamos también una copia de “clus1”. Esto es el equivalente a una copia de seguridad completa.

/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

Ahora podemos iniciar el clúster:

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

Agreguemos algunos datos.

/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

Tenga en cuenta que ahora son las 14:05. Comprobemos si nuestro comando de archivo funciona:

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

Sí, tenemos un solo archivo de almacenamiento. Nuestro último cambio fue a las 14:05, ahora esperemos unos minutos y luego hagamos más cambios.

/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

Entonces ahora hemos agregado 100 filas más, a las 14:16. Detengamos el servidor:

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

y revisa nuestro archivo nuevamente:

/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

Se ve bien. Ahora intentaremos hacer una recuperación PITR de clus2 hasta el momento 14:10.

Primero editemos postgres.conf de clus2 y agreguemos estas líneas al final:

port = 6001
archive_mode = off

Para reproducir los archivos WAL, debemos poner el servidor PostgreSQL para clus2 (que aún no hemos iniciado) en modo de recuperación. Para hacer esto, cree el archivo llamado "recovery.conf" en clus2:

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

Esto contiene el restore_command que hace lo contrario del anterior archive_command , es decir, copiar el archivo solicitado del directorio de archivo al directorio pg_wal.

También hemos establecido el recovery_target_time a las 14:10.

Ahora comenzamos clus2:

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

Para ver qué sucedió, examinemos el archivo de 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.

La recuperación fue rápida (en la vida real, puede demorar horas o días) y el registro indica que se detuvo antes de una transacción en particular (que tiene una marca de tiempo de> 14:10). También dice que la recuperación está en pausa y debe continuarse manualmente.

Examinemos los datos:

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

Vemos que solo hay 10000 filas. A las 14:16 habíamos añadido 100 más, que no han aparecido en la tabla.

Esto se ve bien, así que reanudemos:

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

(1 row)

El archivo de registro ahora informa que la recuperación se completó y se restauraron las operaciones normales:

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

¡Y hemos recuperado con éxito el clúster hasta un tiempo específico!

Lecturas adicionales

Estos son algunos puntos de partida para descubrir más sobre el archivado WAL, el modo de recuperación y PITR:

  • Documentos:configuración de recuperación
  • Documentos:archivado continuo y PITR
  • Capítulo 9 del libro "The Internals ofPostgreSQL"
  • Herramientas:WAL-E,WAL-G, Barman