sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL incrementele back-up en point-in-time herstel

PostgreSQL wordt standaard geleverd met de mogelijkheid om incrementele back-ups en point-in-time recovery uit te voeren. Lees verder voor meer informatie over de instellingen en procedures om dit te bereiken.

Het begint met WAL-bestanden

WAL staat voor Write Ahead Log . WAL's worden in bijna alle moderne RDBMS-systemen gebruikt om duurzame en atomaire transacties te bieden.

Wijzigingen in de gegevens in een PostgreSQL-databasecluster beheerd door een enkel PostgreSQL-serverproces zijn alleen mogelijk via transacties. De wijzigingen die door transacties in de gegevens worden aangebracht, worden geregistreerd als een geordende reeks van WAL-records . Deze records worden weggeschreven naar bestanden met een vaste lengte, genaamd WAL-segmentbestanden , of gewoon WAL-bestanden .

WAL-bestanden leven in $PGDATA/pg_wal , waarbij $PGDATA is de gegevensdirectory voor het databasecluster. Op een standaard Debian-installatie is de WAL-bestandsmap voor het hoofdcluster bijvoorbeeld /var/lib/postgresql/10/main/pg_wal . Hier is hoe het eruit ziet:

# 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-bestanden worden stapsgewijs gegenereerd, in volgorde, beginnend bij het maken van clusters. Ze worden gegenereerd zolang er wijzigingen in de cluster plaatsvinden. Het WAL-bestandsmechanisme is essentieel voor de werking van PostgreSQL en kan niet worden uitgeschakeld.

Nadat de wijzigingen eerst als WAL-records zijn weggeschreven, moeten ze worden toegepast op de weergave op schijf van de gegevens zelf. Dit proces heetcheckpointing , en gebeurt automatisch op de achtergrond (het kan ook handmatig worden afgedwongen). Het punt tot waar de controle werd uitgevoerd, wordt hetREDO-punt . genoemd . Checkpointing is ook een essentieel onderdeel van de Postgres-architectuur en kan niet worden uitgeschakeld.

WAL-bestandsretentie

Tijdens de normale werking van de PostgreSQL-server worden WAL-bestanden steeds geschreven in de pg_wal map. Maar waarom heb je ze in de buurt?

Een reden is crashherstel. Als de PostgreSQL-server crasht en opnieuw opstart, begint hij met het toepassen van wijzigingen van WAL-records in de gegevensbestanden (checkpointing) sinds het laatste REDO-punt. Dit garandeert dat de gegevensbestanden consistent zijn met de laatste voltooide transactie.

Een andere reden houdt verband met streamingreplicatie. Streaming-replicatie werkt door WAL-records naar stand-by te sturen servers, die deze lokaal opslaan en controlepunten uitvoeren. Stand-by's kunnen achterlopen op de server waarvan ze repliceren (de primaire genoemd) ). Als de primaire bijvoorbeeld 100 WALrecords heeft gegenereerd en de standby de eerste 80 heeft ontvangen en toegepast, moeten de meest recente20 beschikbaar zijn zodat de standby kan ontvangen en toepassen vanaf record 81 en later.

Maar de zeer oude WAL-bestanden kunnen zeker worden verwijderd? Ja. PostgreSQL kan worden geïnstrueerd om de meest recente WAL-bestanden te behouden en de oudere te verwijderen. Er zijn drie relevante configuratie-opties:

  • wal_keep_segments - stelt het minimum aantal meest recente WAL-bestanden in dat moet worden bewaard in de WAL-bestandsdirectory
  • max_wal_size - specificeert de maximale totale grootte van WAL-bestanden in de WAL-bestandsdirectory. Als dit wordt overschreden, worden oudere verwijderd. Er kunnen echter redenen zijn (waaronder een hoge waarde voor wal_keep_segments ) die kan voorkomen dat deze instelling wordt gerespecteerd.
  • min_wal_size - specificeert een minimale totale grootte voor WAL-bestanden. Zolang de werkelijke grootte onder deze waarde blijft, worden er geen bestanden verwijderd.

In het echte leven is het niet mogelijk of vereist om alle eerdere WAL-bestanden op te slaan onder de pg_wal directory.

WAL-bestandsarchivering

De echte waarde van WAL-bestanden is dat ze een stroom van wijzigingen zijn die kunnen worden opgenomen en afgespeeld om een ​​consistente replica van een PostgreSQL-cluster te krijgen. PostgreSQL biedt een manier waarop we elk WAL-bestand kunnen kopiëren (of "archiveren") nadat het is aangemaakt – het archive_command configuratie-optie.

Deze optie specificeert een shell-opdrachtreeks die wordt aangeroepen nadat elk WAL-bestand is gemaakt. Hier zijn enkele voorbeelden:

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

Er zijn ook 2 andere opties die moeten worden ingesteld:

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

U kunt de WAL-bestanden comprimeren voordat u ze naar een langdurige/veilige opslaglocatie kopieert. Er is echter een optie genaamd wal_compression . Als u dit inschakelt, zal PostgreSQL de afzonderlijke WAL-records in de WAL-bestanden comprimeren. De WAL-bestanden zelf hebben dezelfde grootte (meestal 16 MB), maar bevatten een reeks gecomprimeerde records in plaats van gewone records.

Continu archiveren

WAL-archivering wordt ook wel continue archivering genoemd en is van kracht,incrementele back-up .

Voordat dit proces van incrementele back-up wordt gestart, is een volledige back-up vereist. Dit stelt een basislijn vast waarop WAL-bestanden incrementeel kunnen worden hersteld. Een volledige back-up kan worden gemaakt door:

  • het Postgres-serverproces afsluiten en de clustergegevensdirectory kopiëren (met behoud van machtigingen), of
  • met behulp van de pg_basebackup op een actieve Postgres-server.

Point-In-Time-Recovery (PITR)

PITR verwijst naar de mogelijkheid van PostgreSQL om te beginnen met het terugzetten van een volledige back-up en vervolgens geleidelijk gearchiveerde WAL-bestanden op te halen en toe te passen tot een gespecificeerd tijdstempel.

Om dit te doen, moeten we een bestand met de naam "recovery.conf" maken in de gegevensmap van het herstelde cluster en een Postgres-server opstarten voor die gegevensmap. Het bestand recovery.conf bevat het doeltijdstempel en ziet er als volgt uit:

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

Het restore_command specificeert hoe een WAL-bestand moet worden opgehaald dat vereist is door PostgreSQL. Het is het omgekeerde van archive_command. De recovery_target_time specificeert de tijd tot wanneer we de wijzigingen nodig hebben.

Wanneer een PostgreSQL-serverproces opstart en een recovery.conf ontdekt bestand in de gegevensmap, start het op in een speciale modus genaamd "herstelmodus". In de herstelmodus worden clientverbindingen geweigerd. Postgres haalt WAL-bestanden op en past ze toe totdat het hersteldoel (in dit geval verandert tot de opgegeven tijdstempel) is bereikt. Wanneer het doel is bereikt, pauzeert de server standaard WAL-replay (andere acties zijn mogelijk). Op dit punt wordt u verondersteld de staat van het herstel te onderzoeken en als alles er goed uitziet, kunt u de pauze ongedaan maken om de herstelmodus te verlaten en door te gaan met de normale werking.

Alles samenvoegen

Dat was allemaal een hele hoop theorie en tekst, laten we het eens proberen om te zien hoe het in de praktijk werkt.

Laten we eerst een nieuw cluster initialiseren:

/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

We zullen ook een map maken die zal dienen als onze veilige opslaglocatie. Laten we dit "archief" noemen.

/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

We moeten de archiefinstellingen configureren die we eerder hebben besproken, voordat we de server kunnen starten. Dus laten we het volgende toevoegen aan het einde vanclus1/postgres.conf :

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

Ons archiefcommando kopieert eenvoudig het WAL-bestand naar de archiefmap die we eerder hebben gemaakt.

We hebben ook de archive_timeout . toegevoegd instelling. Gewoonlijk wordt een WAL-bestand alleen aangemaakt als er voldoende WAL-records zijn om een ​​WAL-bestand van 16 MB te vullen. Dit betekent dat voor servers met weinig schrijfbewerkingen, u mogelijk lang moet wachten voordat een WAL-bestand is gemaakt. De instelling archive_timeout vertelt Postgres dat het moet maak om de zoveel seconden een WAL-bestand aan, ongeacht of het vol is of niet.

Hier hebben we dit ingesteld op 60 (seconden), maar dit is alleen voor de demo! Normaal gesproken wil je het nooit zo laag houden.

Laten we ook een kopie maken van "clus1". Dit is het equivalent van een volledige back-up.

/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

Nu kunnen we het cluster starten:

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

Laten we wat gegevens toevoegen.

/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

Merk op dat de tijd nu 14:05 is. Laten we eens kijken of ons archiefcommando werkt:

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

Ja, we hebben één enkel archiefbestand. Onze laatste wijziging was om 14:05, laten we nu een paar minuten wachten en dan nog wat wijzigingen aanbrengen.

/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

Dus nu hebben we 100 extra rijen toegevoegd, om 14:16. Laten we de server stoppen:

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

en bekijk ons ​​archief opnieuw:

/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

Ziet er goed uit. Nu zullen we proberen een PITR-herstel van clus2 uit te voeren tot 14:10.

Laten we eerst postgres.conf van clus2 bewerken en deze regels aan het einde toevoegen:

port = 6001
archive_mode = off

Om de WAL-bestanden opnieuw af te spelen, moeten we de PostgreSQL-server voor clus2 (die we nog niet hebben gestart) in de herstelmodus zetten. Maak hiervoor het bestand "recovery.conf" aan 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'

Dit bevat het restore_command wat het tegenovergestelde doet van het eerderearchive_command , namelijk het kopiëren van het gevraagde bestand van de archiefmap naar de pg_wal map.

We hebben ook de recovery_target_time . ingesteld tot 14:10.

Nu beginnen we met clus2:

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

Laten we het logbestand bekijken om te zien wat er is gebeurd:

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

Het herstel was snel (in het echte leven kan het uren of dagen duren) en de logstaten dat het is gestopt voor een bepaalde transactie (die een tijdstempel van> 14:10 heeft). Er staat ook dat het herstel is onderbroken en handmatig moet worden voortgezet.

Laten we de gegevens eens bekijken:

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

We zien dat er maar 10000 rijen zijn. Om 14:16 hadden we er nog 100 toegevoegd, die niet in de tabel zijn verschenen.

Dit ziet er goed uit, dus laten we verder gaan:

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

(1 row)

Het logbestand meldt nu dat het herstel is voltooid en dat de normale bewerkingen zijn hersteld:

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

En we hebben het cluster met succes hersteld tot een bepaalde tijd!

Verder lezen

Hier zijn een paar uitgangspunten om meer te ontdekken over WAL-archivering, herstelmodus en PITR:

  • Documenten:RecoveryConfiguration
  • Documenten:continu archiveren en PITR
  • Hoofdstuk 9 uit het boek "The Internals ofPostgreSQL"
  • Gereedschap:WAL-E,WAL-G, Barman

  1. ScaleGrid DBaaS breidt MySQL-hostingservices uit via AWS Cloud

  2. De waarde van de identiteitskolom retourneren na invoeging in Oracle

  3. Een entiteitsveld toewijzen waarvan de naam een ​​gereserveerd woord is in JPA

  4. Waarom levert een geparametriseerde query een veel langzamer queryplan op dan een niet-geparametriseerde query?