PostgreSQL идва с възможността за поетапно архивиране и възстановяване в момента извън кутията. Прочетете, за да научите повече за настройките и процедурите за постигане на това.
Започва с WAL файлове
WAL означава Записване напред . WAL се използват в почти всички съвременни RDBMS системи за осигуряване на трайни и атомарни транзакции.
Промените в данните, съдържащи се в клъстер от база данни PostgreSQL, управляван от отделен процес на PostgreSQL сървър, са възможни само чрез транзакции. Модификациите, направени в данните чрез транзакции, се записват като подредена последователност от WAL записи . Тези записи се записват във файлове с фиксирана дължина, наречени WAL сегментни файлове , или просто WAL файлове .
WAL файловете живеят в $PGDATA/pg_wal
, където $PGDATA
е директорията с данни за клъстера на базата данни. При инсталация на Debian по подразбиране, например, файловата директория WAL за основния клъстер е /var/lib/postgresql/10/main/pg_wal
. Ето как изглежда:
# 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 файловете се генерират постепенно, последователно, като се започне от създаването на клъстер. Те продължават да се генерират, докато се случват модификации на клъстера. Механизмът на WAL файла е от съществено значение за работата на PostgreSQL и не може да бъде изключен.
След като промените за първи път бъдат записани като WAL записи, те трябва да бъдат приложени към представянето на самите данни на диска. Този процес се наричаконтролна точка , и се случва във фонов режим автоматично (може също да бъде наложено ръчно). Точката, до която е извършена контролната точка, се наричаТочка REDO . Контролните точки също са съществена част от архитектурата на Postgres и не могат да бъдат изключени.
Запазване на WAL файл
При нормална работа на PostgreSQL сървъра, WAL файловете ще продължат да се записват в pg_wal
директория. Но защо ги има наоколо?
Една от причините е възстановяването при срив. Ако PostgreSQL сървърът се срине и се рестартира, той започва да прилага промени от WAL записи към файловете с данни (контролна точка) от последната точка на REDO. Това гарантира, че файловете с данни са в съответствие с последната завършена транзакция.
Друга причина е свързана с поточно репликация. Поточното репликация работи чрез изпращане на WAL записи в готовност сървъри, които съхраняват тези локално и изпълняват контролни точки. Режимите на готовност могат да изостават от сървъра, от който се репликират (наречен основен ). Например, ако основният е генерирал 100 WAL записа и режимът на готовност е получил и приложил първите 80, най-новите 20 трябва да са налични, за да може резервният режим да получава и прилага от запис 81 нататък.
Но със сигурност много старите WAL файлове могат да бъдат изтрити? да. PostgreSQL може да бъде инструктиран да запази най-новите WAL файлове и да изтрие по-старите. Има три подходящи опции за конфигурация:
- wal_keep_segments - задава минималния брой най-скорошни WAL файлове, които да бъдат запазени в файловата директория на WAL
- max_wal_size - определя максималния общ размер на WAL файловете във файловата директория WAL. Ако това е превишено, по-старите се изтриват. Въпреки това може да има причини (включително висока стойност за
wal_keep_segments
), което може да предотврати спазването на тази настройка. - min_wal_size - определя минимален общ размер за WAL файлове. Докато действителният размер остава под тази стойност, няма да бъдат изтрити файлове.
В реалния живот не е възможно или задължително да се съхраняват всички предишни WAL файлове под pg_wal
директория.
Архивиране на WAL файлове
Истинската стойност на WAL файловете е, че те са поток от промени, които могат да бъдат записани и възпроизвеждани, за да се получи последователна реплика на PostgreSQL клъстер. PostgreSQL предоставя начин, по който можем да копираме (или „архивираме“) всеки WAL файл, след като получи създаден – архивна_команда опция за конфигурация.
Тази опция определя команден низ на обвивката, който се извиква след създаване на всеки WAL файл. Ето няколко примера:
# 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'
Има и 2 други опции, които трябва да бъдат зададени:
# 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
Можете да компресирате WAL файловете, преди да ги копирате в дългосрочно/безопасно място за съхранение. Има обаче опция, наречена wal_compression . Включването на това ще накара PostgreSQL да компресира отделните WAL записи в WAL файловете. Самите WAL файлове ще бъдат със същия размер (обикновено 16 MB), но ще съдържат поредица от компресирани записи, а не обикновени записи.
Непрекъснато архивиране
WAL архивирането се нарича още непрекъснато архивиране и е в сила,инкрементално архивиране .
Преди да започнете този процес на инкрементално архивиране, е необходимо пълно архивиране. Това установява базова линия, на която WAL файловете могат да бъдат постепенно възстановени. Пълно архивиране може да се направи или от:
- изключване на сървърния процес на Postgres и копиране на директорията с данни на клъстера (при запазване на разрешения) или
- с помощта на
pg_basebackup
на работещ сървър Postgres.
Възстановяване във времето (PITR)
PITR се отнася до способността на PostgreSQL да започне от възстановяването на пълен архив, след което постепенно да извлича и прилага архивирани WAL файлове до определен времеви печат.
За да направим това, трябва да създадем файл, наречен “recovery.conf” в директорията с данни за възстановен клъстер и да стартираме Postgres сървър за тази директория с данни. Файлът recovery.conf съдържа целевото времеви печат и изглежда така:
restore_command = 'cp /tmp/demo/archive/%f "%p"'
recovery_target_time = '2019-06-04 14:10:00'
командата за_възстановяване указва как да се извлече WAL файл, изискван от PostgreSQL. Това е обратното на archive_command. време_на_цел за възстановяване посочва времето, до когато имаме нужда от промените.
Когато процес на сървър на PostgreSQL стартира и открие recovery.conf файл в директорията с данни, той се стартира в специален режим, наречен "режим на възстановяване". Когато е в режим на възстановяване, клиентските връзки се отказват. Postgres извлича WAL файлове и ги прилага, докато се постигне целта за възстановяване (в този случай се променя до посоченото времеви печат). Когато целта е постигната, сървърът по подразбиране поставя на пауза възпроизвеждането на WAL (възможни са други действия). В този момент трябва да проверите състоянието на възстановяването и ако всичко изглежда наред, нулирайте пауза, за да излезете от режима на възстановяване и да продължите нормалната работа.
Събирам всичко заедно
Всичко това беше цял куп теория и текст, нека го изпробваме, за да видим как работи на практика.
Първо нека инициализираме нов клъстер:
/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
Ще създадем и директория, която ще служи като нашето безопасно място за съхранение. Нека наречем това „архив“.
/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
Трябва да конфигурираме настройките на архива, които обсъдихме по-рано, преди да можем да стартираме сървъра. Така че нека добавим следното в края наclus1/postgres.conf
:
port = 6000
wal_level = logical
archive_mode = on
archive_command = 'cp %p /tmp/demo/archive/%f'
archive_timeout = 60
Нашата команда archive просто копира WAL файла в архивната директория, която създадохме по-рано.
Добавихме и archive_timeout настройка. Обикновено WAL файл се създава само когато има достатъчно WAL записи за попълване на 16 MB WAL файл. Това означава, че за сървъри с малко записи може да се наложи да чакате дълго време за създаване на WAL файл. Настройката archive_timeout казва на Postgres, че трябва създавайте aWAL файл на всеки толкова секунди, независимо дали е пълен или не.
Тук сме задали това на 60 (секунди), но това е само за демонстрацията! Обикновено никога не бихте искали да го поддържате толкова ниско.
Нека също така направим копие на „clus1“. Това е еквивалент на пълно архивиране.
/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
Сега можем да стартираме клъстера:
/tmp/demo$ pg_ctl -D clus1 -l log1 start
waiting for server to start.... done
server started
Нека добавим малко данни.
/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
Имайте предвид, че часът сега е 14:05. Нека проверим дали нашата команда за архивиране работи:
/tmp/demo$ ls -l archive/
total 16384
-rw------- 1 postgres postgres 16777216 Jun 4 14:04 000000010000000000000001
Да, имаме един единствен архивен файл. Последната ни промяна беше в 14:05, нека сега да изчакаме няколко минути и след това да направим още промени.
/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
Така че сега добавихме още 100 реда, в 14:16. Нека спрем сървъра:
/tmp/demo$ pg_ctl -D clus1 stop
waiting for server to shut down.... done
server stopped
/tmp/demo$
и проверете отново нашия архив:
/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
Изглежда добре. Сега ще се опитаме да направим PITR възстановяване на clus2 до 14:10.
Първо нека редактираме postgres.conf на clus2 и да добавим тези редове в края:
port = 6001
archive_mode = off
За да възпроизведем WAL файловете, трябва да поставим PostgreSQL сървъра за clus2 (който още не сме стартирали) в режим на възстановяване. За да направите това, създайте файла, наречен “recovery.conf” в clus2:
/tmp/demo$ cat clus2/recovery.conf
restore_command = 'cp /tmp/demo/archive/%f "%p"'
recovery_target_time = '2019-06-04 14:10:00'
Това съдържа командата за_възстановяване което прави обратното на по-раннатаarchive_command , а именно копиране на искания файл от архивната директория в директорията pg_wal.
Също така сме задали recovery_target_time до 14:10.
Сега започваме clus2:
/tmp/demo$ pg_ctl -D clus2 -l log2 start
waiting for server to start.... done
server started
За да видим какво се е случило, нека разгледаме регистрационния файл:
/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.
Възстановяването беше бързо (в реалния живот може да отнеме часове или дни) и регистрационните данни показват, че е спряно преди конкретна транзакция (която има времева марка> 14:10). Също така пише, че възстановяването е на пауза и трябва да бъде продължено ръчно.
Нека разгледаме данните:
/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)
Виждаме, че има само 10 000 реда. В 14:16 добавихме още 100, които не се появиха в таблицата.
Това изглежда добре, така че нека продължим:
demo=# select pg_wal_replay_resume();
pg_wal_replay_resume
----------------------
(1 row)
Регистрационният файл сега съобщава, че възстановяването е завършено и нормалните операции са възстановени:
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
И ние успешно възстановихме клъстера до определено време!
Допълнително четене
Ето няколко отправни точки, за да откриете повече за WAL архивирането, режима на възстановяване и PITR:
- Документи:RecoveryConfiguration
- Документи:Непрекъснато архивиране и PITR
- Глава 9 от книгата „Вътрешните елементи на PostgreSQL“
- Инструменти:WAL-E,WAL-G, Barman