Bootstrap

PostgreSQL 数据库备份与恢复介绍

防止数据库数据丢失的最重要的方法就是备份。造成数据丢失可能的原因有很多种,例如服务器的硬件损坏,而有的是人为的原因导致的(例如误删数据),还有的就是应用程序的bug导致数据误删。因此关于数据库的备份与恢复的策略和方法,在数据库上线之前就应该有充分的设计和测试。

数据库备份和恢复策略必须针对各种可能出现的灾难性的故障,进行规划和设计。例如硬件设备故障(如硬盘损坏)、基础设施的维护(如操作系统故障)、位置灾难(如数据中心断电)、操作错误(rm -rf 数据文件)、数据损坏(应用程序误删)和合规性要求。

因此对数据库进行备份应该是一个常见的任务,以防止数据丢失。

备份和恢复策略考虑的因素

备份和恢复必须综合考虑和平衡对业务的影响和成本因素,例如:

  • How long will recovery take? 数据库恢复的时间
  • How long do we need to store backup data? 数据库备份
  • How much will data storage cost?
  • Will an outage effect our Brand? 数据库服务中断会有什么影响?
  • Can any part of the database remain operational whilst I recover elsewhere?
  • Do I know when the problem I am recovering from started?

可能导致数据丢失的原因

  • Downtime Scenarios •  Device Failure •  Maintenance •  Site Failure •  Operator Error •  Data Corruption •  Compliance
  • Device Failure •  Loss of Machine •  Loss of Disk •  Loss Of Power
  • Maintenance •  Hardware upgrades •  O/S upgrades
  • Site Failure •  Datacenter Failure •  Network Failure •  Office Break-In
  • Operator Error •  Update error •  Dropped table •  Dropped schema •  Deletion of datafile
  • Data Corruption •  Application introduces poor code and in turn corrupts the data •  Disk level corruption
  • Compliance •  Data Retention Periods •  Readable data •  Writeable data •  Storage of data

Postgresql 数据备份的几种方法

目前Postgresql提供了3种最基本的策略来实现备份。

  • 逻辑备份
  • 物理备份文件系统级别的备份(File system level backup)
  • 连续归档(Continuous Archiving)

逻辑备份(pg_dump)

在Postgresql中提供了pg_dump, pg_dumpall工具进行数据库的逻辑备份。pg_dumpall是备份全库,而pg_dump可以选择一个数据库或部分表进行备份。

特点
  • pg_dump 不会阻塞正常的数据库读写,可以在数据库处于使用状态时进行完整的一致的备份,备份可以简单看作是pg_dump开始运行时的数据库的快照。
  • pg_dump的备份文件可以是SQL脚本,也可以是归档文件。用psql执行SQL脚本文件可以重建该数据库,甚至不依赖特定的基础设施(例如操作系统,),脚本修改后甚至可以恢复到非postgres数据库。但是如果是归档文件,则只能用pg_store来重建数据库。
  • pg_dump和pg_restore可以选择性的备份或恢复部分表或数据库对象。
  • pg_dumpall 对db cluster中的每个数据库调用pg_dump来完成该工作,还会还转储对所有数据库公用的全局对象(pg_dump不保存这些对象)。 目前这包括适数据库用户和组、表空间以及适合所有数据库的访问权限等属性。注意pg_dumpall只能导出脚本文件。
实战
  1. 当连接一个本地的数据库时,可以使用如下命令:
[postgres@localhost logic]$ pg_dump -U postgres osdba > osdba_dump1.sql
Password: 
[postgres@localhost logic]$ 
[postgres@localhost logic]$ 
[postgres@localhost logic]$ ll
total 84564
-rw-r--r--. 1 postgres postgres 86591748 Oct  6 16:51 osdba_dump1.sql
[postgres@localhost logic]$ head -n 100 osdba_dump1.sql 
--
-- PostgreSQL database dump
--

-- Dumped from database version 13.3
-- Dumped by pg_dump version 13.3

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;

SET default_tablespace = '';

SET default_table_access_method = heap;

--
-- Name: company; Type: TABLE; Schema: public; Owner: postgres
--

CREATE TABLE public.company (
    id integer,
    name character varying(32)
);


ALTER TABLE public.company OWNER TO postgres;

--
-- Name: people; Type: TABLE; Schema: public; Owner: postgres
--

CREATE TABLE public.people (
    id integer,
    name character varying(32),
    age integer,
    grade numeric(4,2),
    birthday date,
    logintime timestamp without time zone
);


ALTER TABLE public.people OWNER TO postgres;

--
-- Data for Name: company; Type: TABLE DATA; Schema: public; Owner: postgres
--

COPY public.company (id, name) FROM stdin;
1	2d5e81d30b201cae28e51c3bda7f1e08
2	1c367e0fe7172addf79903dfd9bdfe06
3	01f09ce43ddea1a8207a33e0c2969297
...
  1. 可以使用pg_dump备份一个远程的数据库。
## 远程到192.168.146.132
[postgres@localhost logic]$ ssh 192.168.146.132
The authenticity of host '192.168.146.132 (192.168.146.132)' can't be established.
ECDSA key fingerprint is SHA256:pg1sNWe72JI56IqPW3ZCAme0pL9mNUJvRNkV8peif4A.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.146.132' (ECDSA) to the list of known hosts.
[email protected]'s password: 
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Thu Oct  7 00:38:32 2021
[postgres@localhost ~]$ ls
backups  data  initdb_postgresql.log
[postgres@localhost ~]$ cd backups/
[postgres@localhost backups]$ ls
[postgres@localhost backups]$ 
[postgres@localhost backups]$ 
[postgres@localhost backups]$ 
[postgres@localhost backups]$ pg_dump -h 192.168.146.131 -U postgres osdba > osdba.sql
Password: 
## 这里查看导出文件第40行到60行的内容
[postgres@localhost backups]$ head -n 60 osdba.sql | tail -n 20
    name character varying(32),
    age integer,
    grade numeric(4,2),
    birthday date,
    logintime timestamp without time zone
);


ALTER TABLE public.people OWNER TO postgres;

--
-- Data for Name: company; Type: TABLE DATA; Schema: public; Owner: postgres
--

COPY public.company (id, name) FROM stdin;
1	2d5e81d30b201cae28e51c3bda7f1e08
2	1c367e0fe7172addf79903dfd9bdfe06
3	01f09ce43ddea1a8207a33e0c2969297
4	e746f65209317296e56b12db276f9bfc
5	2d043d4cb9210c98084bdb00d8f4cfb3
  1. 自定义格式的备份文件 使用-Fc参数,-F就是format的意思。
[postgres@localhost backups]$ pg_dump -Fc -h 192.168.146.131 -U postgres osdba > osdba131.dump
Password: 
[postgres@localhost backups]$ ll -h
total 116M
-rw-r--r--. 1 postgres postgres 33M Oct  7 01:06 osdba131.dump
-rw-r--r--. 1 postgres postgres 83M Oct  7 01:02 osdba.sql

把osdba131.dump文件恢复到132这台机器的osdba2数据库中。

[postgres@localhost backups]$ createdb -E 'UTF-8' osdba2 ; 
Password: 
[postgres@localhost backups]$ 
[postgres@localhost backups]$ pg_restore -d osdba2 osdba131.dump 
Password: 
[postgres@localhost backups]$ createdb -E 'UTF-8' osdba2 ; 
Password: 
[postgres@localhost backups]$ 
[postgres@localhost backups]$ pg_restore -d osdba2 osdba131.dump 
Password: 
[postgres@localhost backups]$ psql osdba2
Password for user postgres: 
psql (13.3)
Type "help" for help.

osdba2=# select count(0) from people;
  count  
---------
 1000000
(1 row)

osdba2=# select count(0) from company;
 count 
-------
   100
(1 row)

osdba2=# 

  1. 备份指定的表 使用-t 和 -T 用来设置要include与exclude的表。
[postgres@localhost backups]$ pg_dump -t 'company' -h 192.168.146.131 -U postgres osdba > osdba131-company.dump
Password: 
[postgres@localhost backups]$ 
[postgres@localhost backups]$ 
[postgres@localhost backups]$ ll -h
total 116M
-rw-r--r--. 1 postgres postgres 4.4K Oct  7 01:13 osdba131-company.dump
-rw-r--r--. 1 postgres postgres  33M Oct  7 01:06 osdba131.dump
-rw-r--r--. 1 postgres postgres  83M Oct  7 01:02 osdba.sql

  1. pg_dumpall的简单使用
[postgres@localhost dump]$ pg_dumpall -h 127.0.0.1 -p 5432 -U postgres -c -f all_db_bak.sql
Password: 
Password: 
Password: 
[postgres@localhost dump]$ pg_dump -h 127.0.0.1 -p 5432 -U postgres -c -C -f postgres_db_bak.sql postgres
Password: 
[postgres@localhost dump]$ ll -h
total 821M
-rw-r--r--. 1 postgres postgres 309M Oct  6 16:14 all_db_bak.sql
-rw-r--r--. 1 postgres postgres 309M Oct  6 16:15 postgres_db_bak.sql
[postgres@localhost dump]$ wc -l all_db_bak.sql 
1910237 all_db_bak.sql
[postgres@localhost dump]$ wc -l postgres_db_bak.sql 
1910104 postgres_db_bak.sql
[postgres@localhost dump]$ 

大型数据库pg_dump使用

在一些旧的操作系统,会对文件的大小有一定的限制,例如windows XP的NTFS文件系统限定文件最大为64G。Linux中ext2文件系统的最大文件为2TB,ext3单文件最大16TB。如果数据库非常巨大,pg_dump可能因为操作系统的限制导致备份失败。

一些Linux的工具可以实现文件的压缩和分割。

pg_dump dbname | gzip > filename.gz
pg_dump dbname | split -b 1m - filename

物理备份

最简单的物理备份就是冷备份。所谓冷备份就是先把数据库停下来,然后拷贝数据库的PGDATA目录。因为PostgreSQL把实例相关的配置文件和数据文件都放在PGDATA目录下,所以冷备份很简单,就是文件拷贝。

物理冷备份

下面很简单的演示冷备份。

## 停止数据库服务
[jack@localhost ~]$ sudo systemctl stop postgresql.service 
[sudo] password for jack: 
[jack@localhost ~]$ systemctl status postgresql.service 
● postgresql.service - PostgreSQL database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; vendor >
   Active: inactive (dead) since Thu 2021-10-07 01:44:58 CST; 2s ago
  Process: 37220 ExecStart=/usr/bin/postmaster -D ${PGDATA} (code=exited, statu>
  Process: 37217 ExecStartPre=/usr/libexec/postgresql-check-db-dir postgresql (>
 Main PID: 37220 (code=exited, status=0/SUCCESS)
## 备份${PGDATA}文件夹
[jack@localhost ~]$ su - postgres 
Password: 
[postgres@localhost ~]$ 
[postgres@localhost ~]$ echo ${PGDATA}
/var/lib/pgsql/data
[postgres@localhost ~]$ 
[postgres@localhost ~]$ 
[postgres@localhost ~]$ cd backups/
[postgres@localhost backups]$ pwd
/var/lib/pgsql/backups
## 备份文件夹
[postgres@localhost backups]$ tar -cf backup.tar /var/lib/pgsql/data
tar: Removing leading \`/\' from member names
[postgres@localhost backups]$ ll
total 323540
-rw-r--r--. 1 postgres postgres 210452480 Oct  7 01:47 backup.tar
-rw-r--r--. 1 postgres postgres      4483 Oct  7 01:13 osdba131-company.dump
-rw-r--r--. 1 postgres postgres  34249605 Oct  7 01:06 osdba131.dump
-rw-r--r--. 1 postgres postgres  86591748 Oct  7 01:02 osdba.sql
[postgres@localhost backups]$ 
## 恢复
[postgres@localhost ~]$ tar -xvf backups/backup.tar
[postgres@localhost ~]$ ls
backups  initdb_postgresql.log  var
[postgres@localhost ~]$ mv var/lib/pgsql/data/ ./
postgres@localhost ~]$ rm -rf var
[postgres@localhost ~]$ ls
backups  data  initdb_postgresql.log

## 重启服务
[jack@localhost ~]$ sudo systemctl start postgresql.service 
[sudo] password for jack: 

[jack@localhost ~]$ psql -U postgres osdba2
Password for user postgres: 
psql (13.3)
Type "help" for help.

osdba2=# 
osdba2=# 
osdba2=# select count(0) from people;
  count  
---------
 1000000
(1 row)

冷备份必须要先停止数据库,以避免数据不一致的情况,例如WAL写进程正在写一半,造成数据库的不完整性。然后物理冷备份不能实现按数据库对象进行选择性备份。

物理热备份

还有一种物理备份的方法是在不停止数据库的情况下完成,称之为热备份或在线备份。在Postgres中通常的热备份有以下两种方法:

  • 连续归档与PIRT
  • 使用文件系统或块设备级别的快照功能,该方法要求数据库建立在LVM上。
连续归档与PIRT

我们知道PostgreSQL在data目录的pg_wal子目录(10版本前是pg_xlog)中始终维护一份WAL日志文件。该日志文件记录了数据库数据文件的每次改变。当数据库异常崩溃以后,能够通过重放(replay)最后一次检查点(checkpoint)之后的日志记录,就能把数据库推到最终一致的状态。因此我们如果有了最初的数据库备份Base,再加上此备份时间点后的所有数据库的WAL日志,相当于数据库的改变量Δ,然后直接重放Δ就能实现数据库的恢复。

具体来说就是把一个文件系统级别的全量备份和WAL(预写式日志)级别的增量备份结合起来。当需要恢复时,我们先恢复文件系统级别的备份,然后重放备份的WAL文件,把系统恢复到之前的某个状态。

虽然直接复制数据库数据文件,由于复制文件的时间有先后,可能会导致数据文件之间存在不一致的情况。但是由于有了WAL日志,即使备份出来的数据块不一致,也可以通过重放来实现最终的一致性。

连续归档有以下的几个特点:

  • 不需要完美的一致性的备份,备份中的任何非一致性数据都可以通过重放WAL日志文件得以纠正。
  • 可以结合一个无穷长的WAL日志序列用于重放,可以通过简单的归档WAL文件来达到连续备份。
  • 不需要重放WAL日志到最后,可以在任何点停止重放,并使数据库恢复到某个时间点的一致性状态,这就是基于时间点的备份,英文为"Point-In-Time Recovery"简称“PITR”。
  • 可以连续的将一系列WAL文件输送给另外一台已经载入了相同基础备份文件的机器,得到一个实时的热备份系统。

把WAL日志传送到另外一台机器的方法有2种,一种是通过WAL归档日志方法;另外一种是PostgreSQL9.X以后开始提供的被称为流复制的方法。在后续的文章种将详细介绍流复制。

基础备份

使用简单的cp命令或其他命令把数据库在线复制出来的备份,称为基础备份,从基础备份操作开始之后产生的WAL日志和基础备份本身就构成了一个完整的备份。我们当然也可以直接cp的方式来备份,但是PosggreSQL提供了pg_basebackup命令行工具来实现更加灵活和安全的方式完成基础备份。

pg_basebackup工具把整个数据库实例的全部数据都物理的复制出来(包括配置文件),而不是也不能是只把实例种的部分内容单独备份出来例如只备份某些表。该工具使用流复制的协议连接到主数据库上,所以数据库上必须允许replication连接。

pg_hba.conf

host    replication     all             0.0.0.0/0               md5

下面简单介绍其用法:

  1. 备份本机的数据库

-D 指定备份的目标目录,可以不用预先创建。 -Ft 指定备份文件的格式为tar,相当于把备份文件输出到一个tar文件中。 -z 仅仅与tar输出模式配合使用,就是对tar文件进行gzip压缩,便于传输。 -P 用来输出备份的进度

pg_basebackup -D base20211007 -Ft -z -P

[postgres@raw140 backups]$ pwd
/var/lib/pgsql/backups
[postgres@raw140 backups]$ pg_basebackup -D base20211007 -Ft -z -P
479078/479078 kB (100%), 1/1 tablespace
[postgres@raw140 backups]$ ls
base20211007
[postgres@raw140 backups]$ cd base20211007/
[postgres@raw140 base20211007]$ ll -h
total 188M
-rw------- 1 postgres postgres 133K Oct  6 23:22 backup_manifest
-rw------- 1 postgres postgres 188M Oct  6 23:22 base.tar.gz
-rw------- 1 postgres postgres  17K Oct  6 23:22 pg_wal.tar.gz
[postgres@raw140 base20211007]$ 

这里backup_manifest文件是备份的元文件,包括备份的一些基础信息,例如CRC校验等等。

  1. 远程备份
    下面这个例子在141这台机器上备份140机器上的数据库。
    -l label 用来指定备份的一个标识。便于以后维护人员识别该备份。
pg_basebackup -h 192.168.203.140 -Upostgres -Ft -z -P -D pg140_base_backup -l pg140_base_20211007
[postgres@raw141 backups]$ pwd
/var/lib/pgsql/backups
[postgres@raw141 backups]$ pg_basebackup -h 192.168.203.140 -Upostgres -Ft -z -P -D pg140_base_backup -l pg140_base_20211007
Password: 
479078/479078 kB (100%), 1/1 tablespace
[postgres@raw141 backups]$ ll
total 0
drwx------ 2 postgres postgres 69 Oct  6 23:34 pg140_base_backup
[postgres@raw141 backups]$ cd pg140_base_backup/
[postgres@raw141 pg140_base_backup]$ ll -h
total 188M
-rw------- 1 postgres postgres 133K Oct  6 23:34 backup_manifest
-rw------- 1 postgres postgres 188M Oct  6 23:34 base.tar.gz
-rw------- 1 postgres postgres  18K Oct  6 23:34 pg_wal.tar.gz

我们可以自己添加一个定时任务,在每天业务空闲的时段,来对数据库进行全量备份。

WAL日志连续归档

在完成了数据库的基础备份之后,还要对数据库WAL日志进行连续归档,因为默认PostgreSQL的WAL文件只有16个大小为16M的段, 当16个seq写完后,PG会重复利用旧的seq并覆盖原先的内容。因此必须在wal的段文件覆盖之前把他备份到其他的地方,这就需要WAL日志归档功能。

所谓把WAL日志归档,其实就是把在线的已写完的WAL日志复制出来。可以通过配置postgresql.conf文件中的archive_mode和archive_command来打开WAL日志归档。其中archive_command就是一个操作系统的命令,该命令把WAL日志文档复制到其他地方,也包括远程的主机。

postgresql.conf

# - Archiving -
archive_mode = on               # enables archiving; off, on, or always
                                # (change requires restart)
archive_command = 'cp %p /var/lib/pgsql/backups/pg_wall_archive/%f '            # command to use to archive a logfile segment
                                # placeholders: %p = path of file to archive
                                #               %f = file name only
                                # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'

archive_mode=on表示打开归档备份,archive_command中的命令就是完成归档日志的备份。命令中的%p表示在线WAL日志的全路径名,%f表示不包括路径的WAL文件名。当然也可以通过scp命令来把归档日志备份到其他机器上。

下面来通过一个小实验简单验证一下日志归档的功能。

打开归档功能后,我们给jack_test表插入大量的数据。

[postgres@raw140 data]$ psql
Password for user postgres: 
psql (13.3)
Type "help" for help.
postgres=#
postgres=# create table jack_test ( id integer primary key not null, name varchar(128), others text );
CREATE TABLE
postgres=# select count(0) from jack_test;
 count 
-------
 10001
(1 row)

postgres=# insert into jack_test (id, name, others)
postgres-# select generate_series(100000,200000) as id,
postgres-# md5(random()::text) as name,
postgres-# random_string(128);
INSERT 0 100001
postgres=# select count(0) from jack_test;
 count  
--------
 110002
(1 row)
postgres=# insert into jack_test (id, name, others)
select generate_series(200001,1000000) as id,
md5(random()::text) as name,
random_string(128);
INSERT 0 800000
postgres=# select count(0) from jack_test;
 count  
--------
 910002
(1 row)

postgres=# 

postgres=# insert into jack_test (id, name, others)
select generate_series(1000001,2000000) as id,
md5(random()::text) as name,
random_string(128);
INSERT 0 1000000
postgres=# select count(0) from jack_test;
  count  
---------
 1910002
(1 row)

postgres=# 

我们对比一下wal日志目录和wal归档目录,发现所有历史的wal日志都已经保存起来,wal目录由于文件重用的关系,只保留了16个文件。

[postgres@localhost pg_wal]$ tree -Lp 1
.
├── [-rw-------]  000000010000000000000011
├── [-rw-------]  000000010000000000000012
├── [-rw-------]  000000010000000000000013
├── [-rw-------]  000000010000000000000014
├── [-rw-------]  000000010000000000000015
├── [-rw-------]  000000010000000000000016
├── [-rw-------]  000000010000000000000017
├── [-rw-------]  000000010000000000000018
├── [-rw-------]  000000010000000000000019
├── [-rw-------]  00000001000000000000001A
├── [-rw-------]  00000001000000000000001B
├── [-rw-------]  00000001000000000000001C
├── [-rw-------]  00000001000000000000001D
├── [-rw-------]  00000001000000000000001E
├── [-rw-------]  00000001000000000000001F
├── [-rw-------]  000000010000000000000020
├── [-rw-------]  000000010000000000000021
├── [-rw-------]  000000010000000000000022
├── [-rw-------]  000000010000000000000023
├── [-rw-------]  000000010000000000000024
├── [-rw-------]  000000010000000000000025
├── [-rw-------]  000000010000000000000026
├── [-rw-------]  000000010000000000000027
├── [-rw-------]  000000010000000000000028
├── [-rw-------]  000000010000000000000029
├── [-rw-------]  00000001000000000000002A
├── [-rw-------]  00000001000000000000002B
├── [-rw-------]  00000001000000000000002C
└── [drwx------]  archive_status

1 directory, 28 files
[postgres@localhost pg_wal]$ 

[postgres@localhost pg_wall_archive]$ pwd
/var/lib/pgsql/backups/pg_wall_archive
[postgres@localhost pg_wall_archive]$ tree -Lp 1
.
├── [-rw-------]  000000010000000000000001
├── [-rw-------]  000000010000000000000002
├── [-rw-------]  000000010000000000000003
├── [-rw-------]  000000010000000000000004
├── [-rw-------]  000000010000000000000005
├── [-rw-------]  000000010000000000000006
├── [-rw-------]  000000010000000000000007
├── [-rw-------]  000000010000000000000008
├── [-rw-------]  000000010000000000000009
├── [-rw-------]  00000001000000000000000A
├── [-rw-------]  00000001000000000000000B
├── [-rw-------]  00000001000000000000000C
├── [-rw-------]  00000001000000000000000D
├── [-rw-------]  00000001000000000000000E
├── [-rw-------]  00000001000000000000000F
├── [-rw-------]  000000010000000000000010
├── [-rw-------]  000000010000000000000011
├── [-rw-------]  000000010000000000000012
├── [-rw-------]  000000010000000000000013
├── [-rw-------]  000000010000000000000014
├── [-rw-------]  000000010000000000000015
├── [-rw-------]  000000010000000000000016
├── [-rw-------]  000000010000000000000017
├── [-rw-------]  000000010000000000000018
├── [-rw-------]  000000010000000000000019
├── [-rw-------]  00000001000000000000001A
├── [-rw-------]  00000001000000000000001B
├── [-rw-------]  00000001000000000000001C
├── [-rw-------]  00000001000000000000001D
├── [-rw-------]  00000001000000000000001E
├── [-rw-------]  00000001000000000000001F
├── [-rw-------]  000000010000000000000020
├── [-rw-------]  000000010000000000000021
└── [-rw-------]  000000010000000000000022

0 directories, 34 files
[postgres@localhost pg_wall_archive]$ 

简单来说要开启日志归档,需要修改如下配置:

  • wal_level 必须设置为archive或replica(高版本)
  • archive_mode 必须设置为on
  • archive_command 必须是有效的归档命令
  1. 总结 实现在线联机热备,需要以下几点
  • 定期基础备份(每天或者每周)
  • 开启WAL日志的连续归档
基础备份+wal日志数据库恢复
物理热备与恢复实战

pg数据库机器: 192.168.203.140 backup机器: 192.168.203.141
Step1. 140开启postgres用户免密登录141。

[postgres@raw140 data]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/var/lib/pgsql/.ssh/id_rsa): 
Created directory '/var/lib/pgsql/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /var/lib/pgsql/.ssh/id_rsa.
Your public key has been saved in /var/lib/pgsql/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:4nVAX2sAOLo05VjVE4/Cx47MSZd297HtYNyvv+iWjDs [email protected]
The key's randomart image is:
+---[RSA 3072]----+
|       o+oo..    |
|      =o oo* .   |
|     * .= O.= .. |
|    = .+ X o o o+|
|   . o. S o   +.+|
|    .. o .   . o.|
|      .     o . o|
|           E +.. |
|           .=o.oo|
+----[SHA256]-----+
[postgres@raw140 data]$ 
[postgres@raw140 data]$ ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/var/lib/pgsql/.ssh/id_rsa.pub"
The authenticity of host '192.168.203.141 (192.168.203.141)' can't be established.
ECDSA key fingerprint is SHA256:lh9uH9d0WGG740IJ+73DSI1Jm7/CW7jxQeeQFd7w4IQ.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
[email protected]'s password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh '[email protected]'"
and check to make sure that only the key(s) you wanted were added.

[postgres@raw140 data]$ ssh 192.168.203.141
Last login: Wed Oct  6 23:17:19 2021
[postgres@raw141 ~]$ 

Step2. 140数据库开启日志归档,并设置归档命令为scp到远程。 开始远程归档前,可以测试一下scp命令是否允许正常,然后确保远程的备份文件夹已经准备好。

## postgresql.conf
archive_mode = on               # enables archiving; off, on, or always
                                # (change requires restart)
archive_command = 'scp %p [email protected]:/var/lib/pgsql/backups/pg140_wal_archive/%f'         # command to use to archive a logfile segment

Step3. 设置pg_hba.conf,允许其他主机允许pg_basebackup连接,重启数据库

host    replication     all             0.0.0.0/0               md5
[jack@raw140 ~]$ sudo systemctl stop postgresql
[jack@raw140 ~]$ sudo systemctl start postgresql

Step4. 在141上执行基础备份。

[postgres@raw141 backups]$ ll
total 4
drwxr-xr-x 2 postgres postgres 4096 Oct  7 02:33 pg140_wal_archive
[postgres@raw141 backups]$ pg_basebackup -h 192.168.203.140 -Upostgres -Ft -z -P -D pg140_base_backup -l pg140_base_20211007
Password: 
923157/923157 kB (100%), 1/1 tablespace
[postgres@raw141 backups]$ ll -h
total 4.0K
drwx------ 2 postgres postgres   69 Oct  7 02:38 pg140_base_backup
drwxr-xr-x 2 postgres postgres 4.0K Oct  7 02:38 pg140_wal_archive

Step5. 在141上恢复数据库 确保141的pg是停止状态。

[jack@raw141 ~]$ sudo systemctl stop postgresql 
[sudo] password for jack: 
[jack@raw141 ~]$ 
[jack@raw141 ~]$ 
[jack@raw141 ~]$ 
[jack@raw141 ~]$ sudo systemctl status postgresql 
● postgresql.service - PostgreSQL database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Thu 2021-10-07 02:54:39 EDT; 53min ago

解压全量备份的包到数据库的目录。

[postgres@raw141 pg140_base_backup]$ ll
total 457748
-rw------- 1 postgres postgres    137054 Oct  7 02:38 backup_manifest
-rw------- 1 postgres postgres 468571788 Oct  7 02:38 base.tar.gz
-rw------- 1 postgres postgres     17729 Oct  7 02:38 pg_wal.tar.gz
[postgres@raw141 pg140_base_backup]$ echo $PGDATA
/var/lib/pgsql/data
## 解压base.tar.gz到主目录
[postgres@raw141 pg140_base_backup]$ tar -zxvf base.tar.gz -C $PGDATA
backup_label
...
## 解压wal文件到pg_wal目录
[postgres@raw141 pg140_base_backup]$ tar -zxvf pg_wal.tar.gz -C $PGDATA/pg_wal
00000001000000000000004D
archive_status/00000001000000000000004D.done
[postgres@raw141 pg140_base_backup]$ 

[postgres@raw141 data]$ vim postgresql.conf
## 在postgresql.conf中取消掉140的wal归档设置
# - Archiving -

# archive_mode = on             # enables archiving; off, on, or always
                                # (change requires restart)
# archive_command = 'scp %p [email protected]:/var/lib/pgsql/backups/pg140_wal_archive/%f'               # command to use to archive a logfile segment
启动141的数据库

Step 6. 在141上应用最新的wal归档

#首先停止141数据库,因为当前数据库的状态是pg_basebackup运行时的状态。如果要想获得较新的更新,就必须把140的wal归档文件在当前数据库能够进行自动重放(replay)。

[jack@raw141 ~]$ sudo systemctl stop postgresql 
[sudo] password for jack: 
[jack@raw141 ~]$ sudo systemctl status postgresql 
● postgresql.service - PostgreSQL database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Thu 2021-10-07 04:00:04 EDT; 5s ago

因此需要修改postgresql.conf参数,添加与恢复相关的参数。我们知道140的wal归档路径为141的/var/lib/pgsql/backups/pg140_wal_archive目录。

restore_command = 'cp /var/lib/pgsql/backups/pg140_wal_archive/%f %p'           # command to use to restore an archived logfile segment
                                # placeholders: %p = path of file to restore
                                #               %f = file name only
                                # e.g. 'cp /mnt/server/archivedir/%f %p'
                                # (change requires restart)
recovery_target_timeline = 'latest'     # 'current', 'latest', or timeline ID
                                # (change requires restart)

在$PGDATA目录下创建一个信号文件

[postgres@raw141 data]$ touch recovery.signal
[postgres@raw141 data]$ 
[postgres@raw141 data]$ ll recovery.signal 
-rw-r--r-- 1 postgres postgres 0 Oct  7 04:17 recovery.signal

启动数据库

[jack@raw141 ~]$ sudo systemctl start postgresql 
[sudo] password for jack: 
[jack@raw141 ~]$ 
[jack@raw141 ~]$ 
[jack@raw141 ~]$ 
[jack@raw141 ~]$ psql -U postgres
Password for user postgres: 
psql (13.3)
Type "help" for help.

postgres=# 
postgres=# select count(0) from jack_test;
  count  
---------
 2000000
(1 row)
;