sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL inkrementell säkerhetskopiering och punkt-i-tid-återställning

PostgreSQL kommer med möjligheten att göra inkrementella säkerhetskopieringar och punkt-in-timeråterställning direkt ur lådan. Läs vidare för att lära dig mer om inställningarna och procedurerna för att uppnå detta.

Det börjar med WAL-filer

WAL står för Write Ahead Log . WALs används i nästan alla moderna RDBMS-system för att tillhandahålla hållbara och atomära transaktioner.

Ändringar av data som finns i ett PostgreSQL-databaskluster som hanteras av en enda PostgreSQL-serverprocess är endast möjliga via transaktioner. Ändringarna som görs av data av transaktioner registreras som en beställd sekvens av WAL-poster . Dessa poster skrivs till filer med fast längd som kallas WAL-segmentfiler , eller helt enkelt WAL-filer .

WAL-filer finns i $PGDATA/pg_wal , där $PGDATA är datakatalogen för databasklustret. På en standard Debianinstallation till exempel, är WAL-filkatalogen för huvudklustret /var/lib/postgresql/10/main/pg_wal . Här ishow ser det ut så här:

# 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-filer genereras inkrementellt, i sekvens, med början från klusterskapandet. De fortsätter att genereras så länge ändringar händer i klustret. WAL-filmekanismen är viktig för att PostgreSQL ska fungera och kan inte stängas av.

Efter att ändringarna först har skrivits ut som WAL-poster måste de tillämpas på återgivningen på disken av själva data. Denna process kallascheckpointing , och händer i bakgrunden automatiskt (det kan också tvingas fram manuellt). Punkten fram till vilken kontrollpunkten gjordes kallasREDO-punkten . Checkpointing är också en viktig del av Postgres arkitektur och kan inte stängas av.

WAL-fillagring

Under normal drift av PostgreSQL-servern kommer WAL-filer att fortsätta att skrivas in i pg_wal katalog. Men varför ha dem i närheten?

En anledning är kraschåterställning. Om PostgreSQL-servern kraschar och startar om, börjar den tillämpa ändringar från WAL-poster i datafilerna (checkpointing) sedan den senaste REDO-punkten. Detta garanterar att datafilerna överensstämmer med den senast genomförda transaktionen.

En annan anledning är relaterad till strömmande replikering. Strömmande replikering fungerar genom att skicka WAL-poster till standby servrar, som lagrar dessa lokalt och utför kontrollpunkter. Standbylägen kan ligga efter servern de replikerar från (kallad primär ). Till exempel, om den primära har genererat 100 WAL-poster och vänteläget har tagit emot och tillämpat de första 80, måste de senaste 20 vara tillgängliga så att vänteläget kan ta emot och tillämpas från post 81 och framåt.

Men visst kan de mycket gamla WAL-filerna tas bort? Ja. PostgreSQL kan instrueras att behålla de senaste WAL-filerna och ta bort de äldre. Det finns tre relevanta konfigurationsalternativ:

  • wal_keep_segments - ställer in det minsta antalet senaste WAL-filer som ska behållas i WAL-filkatalogen
  • max_wal_size - anger den maximala totala storleken på WAL-filer i WAL-filkatalogen. Om detta överskrids raderas äldre. Det kan dock finnas anledningar (inklusive ett högt värde för wal_keep_segments ) som kan förhindra att den här inställningen respekteras.
  • min_wal_size - anger en minsta totalstorlek för WAL-filer. Så länge den faktiska storleken förblir under detta värde kommer inga filer att raderas.

I verkligheten är det inte möjligt eller nödvändigt att lagra alla tidigare WAL-filer under pg_wal katalog.

WAL-filarkivering

Det verkliga värdet av WAL-filer är att de är en ström av ändringar som kan spelas in och spelas upp igen för att få en konsekvent kopia av ett PostgreSQL-kluster. PostgreSQL tillhandahåller ett sätt på vilket vi kan kopiera ut (eller "arkivera") varje WAL-fil efter att den har hämtats skapad – archive_command konfigurationsalternativ.

Det här alternativet anger en skalkommandosträng som anropas efter att varje WAL-fil har skapats. Här är några exempel:

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

Det finns också två andra alternativ som måste ställas in:

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

Du kan komprimera WAL-filerna innan du kopierar dem till en långsiktig/säker lagringsplats. Det finns dock ett alternativ som heter wal_compression . Om du aktiverar detta kommer PostgreSQL att komprimera de individuella WAL-posterna i WAL-filerna. Själva WAL-filerna kommer att vara av samma storlek (vanligtvis 16 MB), men kommer att innehålla en sekvens av komprimerade poster snarare än vanliga poster.

Kontinuerlig arkivering

WAL-arkivering kallas också kontinuerlig arkivering och är i kraft,inkrementell säkerhetskopiering .

Innan den här processen med inkrementell säkerhetskopiering påbörjas krävs en fullständig säkerhetskopiering. Detta upprättar en baslinje på vilken WAL-filer kan återställas stegvis. En fullständig säkerhetskopiering kan tas antingen genom:

  • att stänga av Postgres-serverprocessen och kopiera klusterdatakatalogen (med bibehållen behörighet), eller
  • med hjälp av pg_basebackup på en körande Postgres-server.

Point-in-Time-Recovery (PITR)

PITR hänvisar till PostgreSQL:s förmåga att starta från återställningen av en fullständig säkerhetskopia och sedan gradvis hämta och tillämpa arkiverade WAL-filer upp till en angiven tidsstämpel.

För att göra detta måste vi skapa en fil som heter "recovery.conf" i den återställda klusterdatakatalogen och starta en Postgres-server för den datakatalogen. Recovery.conf-filen innehåller måltidsstämpeln och ser ut så här:

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

restore_command anger hur man hämtar en WAL-fil som krävs av PostgreSQL. Det är inversen av archive_command. återställningsmåltiden anger tiden tills vi behöver ändringarna.

När en PostgreSQL-serverprocess startar och upptäcker en recovery.conf filen i datakatalogen startar den i ett speciellt läge som kallas "återställningsläge". I återställningsläge nekas klientanslutningar. Postgres hämtar WAL-filer och tillämpar dem tills återställningsmålet (i detta fall ändras upp till den angivna tidsstämpeln) uppnås. När målet är uppnått pausar servern som standard WAL-uppspelning (andra åtgärder är möjliga). Vid det här laget ska du undersöka tillståndet för återställningen och om allt ser ok ut, återuppta paus för att avsluta återställningsläget och fortsätta normal drift.

Lägga ihop allt

Allt det där var en hel massa teori och text, låt oss prova det för att se hur det fungerar i praktiken.

Låt oss först initiera ett nytt kluster:

/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

Vi kommer också att skapa en katalog som kommer att fungera som vår säkra lagringsplats. Låt oss kalla detta "arkiv".

/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

Vi måste konfigurera arkivinställningarna vi diskuterade tidigare innan vi kan starta servern. Så låt oss lägga till följande i slutet av clus1/postgres.conf :

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

Vårt arkivkommando kopierar helt enkelt WAL-filen till arkivkatalogen vi skapade tidigare.

Vi har också lagt till archive_timeout miljö. Vanligtvis skapas en WAL-fil endast när det finns tillräckligt med WAL-poster för att fylla en 16 MB WAL-fil. Detta innebär att för servrar med få skrivningar kan du behöva vänta länge på att en WAL-fil ska skapas. Inställningen archive_timeout säger till Postgres att den måste skapa en WAL-fil var så många sekunder, oavsett om den är full eller inte.

Här har vi satt detta till 60 (sekunder), men detta är bara för demot! Du vill vanligtvis aldrig hålla det så här lågt.

Låt oss också göra en kopia av "clus1". Detta motsvarar en fullständig säkerhetskopia.

/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 kan vi starta klustret:

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

Låt oss lägga till lite data.

/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

Observera att klockan nu är 14:05. Låt oss kontrollera om vårt arkivkommando fungerar:

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

Ja, vi har en enda arkivfil. Vår senaste förändring var kl. 14:05, låt oss nu vänta några minuter och sedan göra några fler ändringar.

/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

Så nu har vi lagt till 100 rader till, klockan 14:16. Låt oss stoppa servern:

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

och kolla vårt arkiv igen:

/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

Ser bra ut. Nu ska vi försöka göra en PITR-återställning av clus2 upp till klockan 14:10.

Låt oss först redigera clus2s postgres.conf och lägga till dessa rader i slutet:

port = 6001
archive_mode = off

För att spela upp WAL-filerna måste vi sätta PostgreSQL-servern för clus2 (som vi inte har startat ännu) i återställningsläge. För att göra detta, skapa filen som heter "recovery.conf" i clus2:

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

Detta innehåller restore_command som gör motsatsen till det tidigarearkivkommandot , nämligen att kopiera den begärda filen från arkivkatalogen till katalogen pg_wal.

Vi har också ställt in återställningsmåltiden till 14:10.

Nu börjar vi clus2:

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

För att se vad som hände, låt oss undersöka loggfilen:

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

Återhämtningen var snabb (i verkligheten kan det ta timmar eller dagar) och loggen anger att den har stoppats före en viss transaktion (som har en tidsstämpel på> 14:10). Det står också att återställningen är pausad och måste fortsätta manuellt.

Låt oss undersöka uppgifterna:

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

Vi ser att det bara finns 10 000 rader. Klockan 14:16 hade vi lagt till 100 till, som inte har visats i tabellen.

Det här ser bra ut, så låt oss återuppta:

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

(1 row)

Loggfilen rapporterar nu att återställningen är klar och normala funktioner återställs:

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

Och vi har framgångsrikt återställt klustret fram till en angiven tidpunkt!

Ytterligare läsning

Här är några utgångspunkter för att upptäcka mer om WAL-arkivering, återställningsläge och PITR:

  • Dokument:RecoveryConfiguration
  • Dokument:Kontinuerlig arkivering och PITR
  • Kapitel 9 från boken "The Internals ofPostgreSQL"
  • Verktyg:WAL-E,WAL-G, Barman

  1. PostgreSQL skickar data från rekursiv CTE till funktion

  2. Flera array_agg()-anrop i en enda fråga

  3. SQL:välja rader där kolumnvärdet ändrats från föregående rad

  4. Anslut SAP IQ till SQL Server