本章将涵盖以下主题:
- 介绍各种类型的备份和恢复
- 探索逻辑备份
- 探索物理备份
介绍备份和恢复类型
PostgreSQL 数据库应定期备份。虽然备份过程本质上很简单,但清晰地了解底层技术和假设至关重要。
备份 PostgreSQL 数据有三种截然不同的方法:
- SQL 转储(逻辑备份)
- 文件系统级备份(物理备份)
- 持续归档
探索逻辑备份
包括一组工具:
- pg_dump — 将 PostgreSQL 数据库提取到脚本文件或其他存档文件中
- pg_dumpall — 将 PostgreSQL 数据库集群提取到脚本文件中
- pg_restore — 从 pg_dump 创建的存档文件中恢复 PostgreSQL 数据库
转储单个数据库
命令就是pg_dump。
💡 pg_dump只能备份单库,不会备份全局对象如role,tablespace。
pg_dump得到的是一致性备份,备份时不会阻塞其他用户访问数据库。
pg_dump输出文件支持4中格式:
- 平文本(默认),即SQL脚本,用psql恢复。可以恢复到其他平台或数据库。
- tar格式。用pg_restore恢复。
- 目录格式。支持选择性和并行恢复。
- 定制格式。支持选择性恢复。
示例:
$ pg_dump -U dbadmin sampledb -f sampledb.dump
sampledb.dump的主要内容如下,重点看其中中文注释部分:
--
-- PostgreSQL database dump
--
-- Dumped from database version 16.9
-- Dumped by pg_dump version 16.9
-- 以下的一组SET命令是给恢复用的
-- 客户端连接中的语句行为参数
-- 中止任何耗时超过指定时间的语句。
SET statement_timeout = 0;
-- 中止任何在尝试获取表、索引、行或其他数据库对象的锁时等待时间超过指定时间的语句。
SET lock_timeout = 0;
-- 终止打开的事务中空闲时间(即等待客户端查询)超过指定时间的任何会话。
SET idle_in_transaction_session_timeout = 0;
-- 设置客户端编码(字符集)。默认使用数据库编码。
SET client_encoding = 'UTF8';
/* 此参数通常为开启。
当设置为关闭时,它将禁用在 CREATE FUNCTION 和 CREATE PROCEDURE
期间对例程主体字符串的验证。禁用验证可以避免验证过程的副作用,
特别是防止由于前向引用等问题导致的误报。在代表其他用户加载函数之前
将此参数设置为关闭;pg_dump 会自动执行此操作。
*/
SET check_function_bodies = false;
SET xmloption = content;
-- 控制向客户端发送哪些级别的消息。
SET client_min_messages = warning;
-- 此变量控制是否引发错误来代替应用行安全策略。
SET row_security = off;
-- 版本和平台兼容性参数
-- 此项控制普通字符串字面量 ('...') 是否按照 SQL 标准中的规定按字面意义处理反斜杠。从 PostgreSQL 9.1 开始,默认启用(之前的版本默认禁用)。
SET standard_conforming_strings = on;
-- 参数设置函数,
-- 最后一个参数设为false,表示在整个会话期间生效,而非仅当前事务
-- 这是一项安全预防措施。以防引用恶意代码。
SELECT pg_catalog.set_config('search_path', '', false);
--
-- Name: hr; Type: SCHEMA; Schema: -; Owner: dbadmin
--
-- schema属于数据库级对象,会自动创建;
-- 但role如dbadmin属于cluster一级对象,则需事先存在
CREATE SCHEMA hr;
ALTER SCHEMA hr OWNER TO dbadmin;
--
-- Name: sh; Type: SCHEMA; Schema: -; Owner: dbadmin
--
CREATE SCHEMA sh;
ALTER SCHEMA sh OWNER TO dbadmin;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: countries; Type: TABLE; Schema: hr; Owner: dbadmin
--
CREATE TABLE hr.countries (
country_id character(2) NOT NULL,
...
-- 数据导入默认使用COPY ... FROM stdin,目的是为了快
-- 也可以改为INSERT语句,以获得更好的可移植性
COPY hr.countries (country_id, country_name, region_id) FROM stdin;
IT Italy 1
JP Japan 3
US United States of America 2
...
恢复单个数据库
使用pg_restore实用程序或restoredb元命令。
但之前备份中并不包含CREATE DATABASE命令,这意味着恢复操作将在您连接的数据库中进行。这需要谨慎从事。
以下选择在一个新建的数据库中恢复:
$ psql
psql (16.9)
Type "help" for help.
postgres=# create database restoredb with owner dbadmin;
CREATE DATABASE
postgres=# \c restoredb dbadmin
Password for user dbadmin:
You are now connected to database "restoredb" as user "dbadmin".
restoredb=> \i sampledb.dump
...
restoredb=> show search_path;
search_path
-------------
(1 row)
-- 由于脚本中search_path设置为'',因此必须使用full qualified name
restoredb=> select count(*) from employees;
ERROR: relation "employees" does not exist
LINE 1: select count(*) from employees;
^
restoredb=> select count(*) from hr.employees;
count
-------
107
(1 row)
restoredb=> \conninfo
You are connected to database "restoredb" as user "dbadmin" via socket in "/run/postgresql" at port "5432".
restoredb=> select current_database();
current_database
------------------
restoredb
(1 row)
如果在命令中加入–create,则会在备份中生成CREATE DATABASE语句:
$ pg_dump -U dbadmin sampledb -f sampledb.dump --create
$ more sampledb.dump
--
-- Name: sampledb; Type: DATABASE; Schema: -; Owner: dbadmin
--
CREATE DATABASE sampledb WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.UTF-8';
ALTER DATABASE sampledb OWNER TO dbadmin;
\connect sampledb
选择性备份
可以使用或组合以下选项:
- -a: 仅数据
- -s: 仅schema,无数据
- -t: 指定表
- -T: 排除指定表
压缩
可以用-Z选项指定9级压缩,只适用于脚本备份。
$ pg_dump -U dbadmin sampledb -f sampledb.dump
$ pg_dump -U dbadmin sampledb -f sampledb.dump.gz -Z 9
$ ls -lh sampledb.dump*
-rw-r--r--. 1 postgres postgres 31K Jun 28 02:01 sampledb.dump
-rw-r--r--. 1 postgres postgres 8.3K Jun 28 02:02 sampledb.dump.gz
转储格式和 pg 恢复
以下为除脚本备份格式之外的其他三种备份格式:
# custome格式,二进制格式
$ pg_dump -U dbadmin sampledb -f sampledb.backup.custom -Fc
# directory格式,二进制格式,有压缩;自动创建目录
# 每个对象一个gz文件,便于并行恢复
$ pg_dump -U dbadmin sampledb -f sampledb.backup.d -Fd
# tar格式,二进制格式,无压缩
$ pg_dump -U dbadmin sampledb -f sampledb.backup.tar -Ft
查看输出:
$ ls -lh sampledb.*
-rw-r--r--. 1 postgres postgres 32K Jun 28 03:14 sampledb.backup.custom
-rw-r--r--. 1 postgres postgres 66K Jun 28 03:16 sampledb.backup.tar
sampledb.backup.d:
total 56K
-rw-r--r--. 1 postgres postgres 75 Jun 28 03:16 3444.dat.gz
-rw-r--r--. 1 postgres postgres 277 Jun 28 03:16 3445.dat.gz
-rw-r--r--. 1 postgres postgres 809 Jun 28 03:16 3446.dat.gz
-rw-r--r--. 1 postgres postgres 362 Jun 28 03:16 3448.dat.gz
-rw-r--r--. 1 postgres postgres 351 Jun 28 03:16 3450.dat.gz
-rw-r--r--. 1 postgres postgres 3.2K Jun 28 03:16 3451.dat.gz
-rw-r--r--. 1 postgres postgres 205 Jun 28 03:16 3453.dat.gz
-rw-r--r--. 1 postgres postgres 26K Jun 28 03:16 toc.dat
恢复示例:
$ psql -c "drop database sampledb;"
# 连接到postgres库是为了创建sampledb库
$ pg_restore -C -d postgres sampledb.backup.d
都恢复了:
sampledb=> \dn
List of schemas
Name | Owner
--------+-------------------
hr | dbadmin
public | pg_database_owner
sh | dbadmin
(3 rows)
sampledb=> set search_path="hr";
SET
sampledb=> \dt
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+---------
hr | countries | table | dbadmin
hr | departments | table | dbadmin
hr | employees | table | dbadmin
hr | job_history | table | dbadmin
hr | jobs | table | dbadmin
hr | locations | table | dbadmin
hr | regions | table | dbadmin
(7 rows)
也可以仅生成恢复脚本,而不执行恢复:
pg_restore sampledb.backup.custom -f resotore.sql
pg_restore sampledb.backup.tar -f resotore.sql
pg_restore sampledb.backup.d -f resotore.sql
执行选择性恢复
列出备份中的对象:
$ pg_restore --list sampledb.backup.d
;
; Archive created at 2025-06-28 03:16:46 UTC
; dbname: sampledb
; TOC Entries: 91
; Compression: gzip
; Dump Version: 1.15-0
; Format: DIRECTORY
; Integer: 4 bytes
; Offset: 8 bytes
; Dumped from database version: 16.9
; Dumped by pg_dump version: 16.9
;
;
; Selected TOC Entries:
;
6; 2615 18783 SCHEMA - hr dbadmin
7; 2615 18784 SCHEMA - sh dbadmin
218; 1259 18798 TABLE hr countries dbadmin
3460; 0 0 COMMENT hr COLUMN countries.country_id dbadmin
3461; 0 0 COMMENT hr COLUMN countries.country_name dbadmin
3462; 0 0 COMMENT hr COLUMN countries.region_id dbadmin
221; 1259 18821 TABLE hr departments dbadmin
3463; 0 0 COMMENT hr COLUMN departments.department_id dbadmin
3464; 0 0 COMMENT hr COLUMN departments.department_name dbadmin
3465; 0 0 COMMENT hr COLUMN departments.manager_id dbadmin
3466; 0 0 COMMENT hr COLUMN departments.location_id dbadmin
222; 1259 18831 SEQUENCE hr departments_seq dbadmin
224; 1259 18837 TABLE hr employees dbadmin
3467; 0 0 COMMENT hr COLUMN employees.employee_id dbadmin
3468; 0 0 COMMENT hr COLUMN employees.first_name dbadmin
3469; 0 0 COMMENT hr COLUMN employees.last_name dbadmin
3470; 0 0 COMMENT hr COLUMN employees.email dbadmin
3471; 0 0 COMMENT hr COLUMN employees.phone_number dbadmin
3472; 0 0 COMMENT hr COLUMN employees.hire_date dbadmin
3473; 0 0 COMMENT hr COLUMN employees.job_id dbadmin
3474; 0 0 COMMENT hr COLUMN employees.salary dbadmin
3475; 0 0 COMMENT hr COLUMN employees.commission_pct dbadmin
3476; 0 0 COMMENT hr COLUMN employees.manager_id dbadmin
3477; 0 0 COMMENT hr COLUMN employees.department_id dbadmin
223; 1259 18832 TABLE hr jobs dbadmin
3478; 0 0 COMMENT hr COLUMN jobs.job_id dbadmin
3479; 0 0 COMMENT hr COLUMN jobs.job_title dbadmin
3480; 0 0 COMMENT hr COLUMN jobs.min_salary dbadmin
3481; 0 0 COMMENT hr COLUMN jobs.max_salary dbadmin
219; 1259 18810 TABLE hr locations dbadmin
3482; 0 0 COMMENT hr TABLE locations dbadmin
3483; 0 0 COMMENT hr COLUMN locations.location_id dbadmin
3484; 0 0 COMMENT hr COLUMN locations.street_address dbadmin
3485; 0 0 COMMENT hr COLUMN locations.postal_code dbadmin
3486; 0 0 COMMENT hr COLUMN locations.city dbadmin
3487; 0 0 COMMENT hr COLUMN locations.state_province dbadmin
3488; 0 0 COMMENT hr COLUMN locations.country_id dbadmin
217; 1259 18791 TABLE hr regions dbadmin
3489; 0 0 COMMENT hr TABLE regions dbadmin
227; 1259 18887 VIEW hr emp_details_view dbadmin
225; 1259 18865 SEQUENCE hr employees_seq dbadmin
226; 1259 18866 TABLE hr job_history dbadmin
3490; 0 0 COMMENT hr COLUMN job_history.employee_id dbadmin
3491; 0 0 COMMENT hr COLUMN job_history.start_date dbadmin
3492; 0 0 COMMENT hr COLUMN job_history.end_date dbadmin
3493; 0 0 COMMENT hr COLUMN job_history.job_id dbadmin
3494; 0 0 COMMENT hr COLUMN job_history.department_id dbadmin
220; 1259 18820 SEQUENCE hr locations_seq dbadmin
3445; 0 18798 TABLE DATA hr countries dbadmin
3448; 0 18821 TABLE DATA hr departments dbadmin
3451; 0 18837 TABLE DATA hr employees dbadmin
3453; 0 18866 TABLE DATA hr job_history dbadmin
3450; 0 18832 TABLE DATA hr jobs dbadmin
3446; 0 18810 TABLE DATA hr locations dbadmin
3444; 0 18791 TABLE DATA hr regions dbadmin
3495; 0 0 SEQUENCE SET hr departments_seq dbadmin
3496; 0 0 SEQUENCE SET hr employees_seq dbadmin
3497; 0 0 SEQUENCE SET hr locations_seq dbadmin
3266; 2606 18804 CONSTRAINT hr countries country_c_id_pk dbadmin
3273; 2606 18825 CONSTRAINT hr departments dept_id_pk dbadmin
3279; 2606 18842 CONSTRAINT hr employees emp_email_uk dbadmin
3281; 2606 18844 CONSTRAINT hr employees emp_emp_id_pk dbadmin
3287; 2606 18871 CONSTRAINT hr job_history jhist_emp_id_st_date_pk dbadmin
3276; 2606 18836 CONSTRAINT hr jobs job_id_pk dbadmin
3270; 2606 18814 CONSTRAINT hr locations loc_id_pk dbadmin
3264; 2606 18797 CONSTRAINT hr regions reg_id_pk dbadmin
3274; 1259 18913 INDEX hr dept_location_ix dbadmin
3277; 1259 18909 INDEX hr emp_department_ix dbadmin
3282; 1259 18910 INDEX hr emp_job_ix dbadmin
3283; 1259 18911 INDEX hr emp_manager_ix dbadmin
3284; 1259 18912 INDEX hr emp_name_ix dbadmin
3285; 1259 18916 INDEX hr jhist_department_ix dbadmin
3288; 1259 18915 INDEX hr jhist_employee_ix dbadmin
3289; 1259 18914 INDEX hr jhist_job_ix dbadmin
3267; 1259 18917 INDEX hr loc_city_ix dbadmin
3268; 1259 18919 INDEX hr loc_country_ix dbadmin
3271; 1259 18918 INDEX hr loc_state_province_ix dbadmin
3290; 2606 18805 FK CONSTRAINT hr countries countr_reg_fk dbadmin
3292; 2606 18826 FK CONSTRAINT hr departments dept_loc_fk dbadmin
3293; 2606 18904 FK CONSTRAINT hr departments dept_mgr_fk dbadmin
3294; 2606 18845 FK CONSTRAINT hr employees emp_dept_fk dbadmin
3295; 2606 18850 FK CONSTRAINT hr employees emp_job_fk dbadmin
3296; 2606 18855 FK CONSTRAINT hr employees emp_manager_fk dbadmin
3297; 2606 18882 FK CONSTRAINT hr job_history jhist_dept_fk dbadmin
3298; 2606 18877 FK CONSTRAINT hr job_history jhist_emp_fk dbadmin
3299; 2606 18872 FK CONSTRAINT hr job_history jhist_job_fk dbadmin
3291; 2606 18815 FK CONSTRAINT hr locations loc_c_id_fk dbadmin
裁剪或修改此列表文件,即可做选择性恢复。
例如:
$ psql -c "drop database sampledb;"
$ cat select.list
; Selected TOC Entries:
;
6; 2615 18783 SCHEMA - hr dbadmin
7; 2615 18784 SCHEMA - sh dbadmin
224; 1259 18837 TABLE hr employees dbadmin
$ pg_restore -C -d postgres -L select.list sampledb.backup.d
仅恢复schema,不要数据:
pg_restore -C -d postgres -s sampledb.backup.d
转储整个集群
用pg_dumpall命令。
$ pg_dumpall -f cluster.sql
$ wc -l cluster.sql
1388001 cluster.sql
pg_dumpall只输出SQL脚本。
只输出cluster一级的对象,包括role,user configuration,role memberships,tablespace:
$ pg_dumpall --globals-only -f cluster.sql
$ wc -l cluster.sql
76 cluster.sql
并行备份
pg_dump的directory格式可通过-j选项指定并行备份。
对于directory和custom格式的备份,pg_restore时可通过-j选项指定并行恢复。
$ pg_dump -Fd -f sampledb.backup.d -v -j 2 sampledb
...
pg_dump: finished item 3447 TABLE DATA employees
pg_dump: dumping contents of table "hr.jobs"
pg_dump: dumping contents of table "hr.departments"
pg_dump: finished item 3448 TABLE DATA jobs
pg_dump: finished item 3445 TABLE DATA departments
...
输出中的finished item
说明启用了并行。
并行恢复:
$ psql -c "DROP DATABASE sampledb;"
$ pg_restore -C -d postgres -j 4 -v sampledb.backup.d
备份自动化
利用crontab。
利用shell脚本从系统表中获取数据库名,然后逐一备份:
postgres=# SELECT datname FROM pg_database;
datname
--------------------
postgres
template1
template0
world_temperatures
demo
restoredb
sampledb
(7 rows)
COPY 命令
COPY — 在文件和表之间复制数据。
需要pg_read_server_files和pg_write_server_files权限。
COPY 命令用于在 PostgreSQL 表和标准文件系统文件之间移动数据。COPY TO 命令将表内容复制到文件,而 COPY FROM 命令将数据从文件复制到表(将数据附加到表中已有的内容)。COPY TO 命令还可以复制 SELECT 查询的结果。
如果指定了列列表,COPY TO 命令只会将指定列中的数据复制到文件。对于 COPY FROM 命令,文件中的每个字段都会按顺序插入到指定的列中。COPY FROM 命令中未指定的表列将采用其默认值。
带有文件名的 COPY 命令指示 PostgreSQL 服务器直接从文件读取或写入文件。当指定 STDIN 或 STDOUT 时,数据将通过客户端和服务器之间的连接传输。
postgres=# \h COPY
Command: COPY
Description: copy data between a file and a table
Syntax:
COPY table_name [ ( column_name [, ...] ) ]
FROM { 'filename' | PROGRAM 'command' | STDIN }
[ [ WITH ] ( option [, ...] ) ]
[ WHERE condition ]
COPY { table_name [ ( column_name [, ...] ) ] | ( query ) }
TO { 'filename' | PROGRAM 'command' | STDOUT }
[ [ WITH ] ( option [, ...] ) ]
where option can be one of:
FORMAT format_name
FREEZE [ boolean ]
DELIMITER 'delimiter_character'
NULL 'null_string'
DEFAULT 'default_string'
HEADER [ boolean | MATCH ]
QUOTE 'quote_character'
ESCAPE 'escape_character'
FORCE_QUOTE { ( column_name [, ...] ) | * }
FORCE_NOT_NULL ( column_name [, ...] )
FORCE_NULL ( column_name [, ...] )
ENCODING 'encoding_name'
URL: https://www.postgresql.org/docs/16/sql-copy.html
示例:
$ psql -U dbadmin sampledb
Password for user dbadmin:
psql (16.9)
Type "help" for help.
sampledb=> select count(*) from hr.employees;
count
-------
107
(1 row)
sampledb=> copy hr.regions to '/tmp/regions.sql';
ERROR: permission denied to COPY to a file
DETAIL: Only roles with privileges of the "pg_write_server_files" role may COPY to a file.
HINT: Anyone can COPY to stdout or from stdin. psql's \copy command also works for anyone.
sampledb=> \c postgres postgres
You are now connected to database "postgres" as user "postgres".
postgres=# grant pg_write_server_files to dbadmin;
GRANT ROLE
postgres=# \c sampledb dbadmin
Password for user dbadmin:
You are now connected to database "sampledb" as user "dbadmin".
sampledb=> copy hr.regions to '/tmp/regions.sql';
COPY 4
查看文件:
$ cat /tmp/regions.sql
1 Europe
2 Americas
3 Asia
4 Middle East and Africa
指定格式:
sampledb=> copy hr.regions to '/tmp/regions.sql' with(header on, delimiter ';');
COPY 4
sampledb=> \! cat /tmp/regions.sql
region_id;region_name
1;Europe
2;Americas
3;Asia
4;Middle East and Africa
备份加恢复:
sampledb=> copy hr.regions to '/tmp/regions.csv' with(format csv);
COPY 4
sampledb=> \! cat /tmp/regions.csv
1,Europe
2,Americas
3,Asia
4,Middle East and Africa
sampledb=> create table hr.regions_bak as select * from hr.regions where 1=2;
SELECT 0
sampledb=> copy hr.regions_bak from '/tmp/regions.csv' with(format csv);
COPY 4
COPY FROM可以指定WHERE条件,但COPY TO不行。不过COPY TO可以指定SQL或视图:
sampledb=> copy (select * from hr.employees where employee_id = 100) to '/tmp/emp100.sql';
COPY 1
也可以从外部应用程序中提取元组,例如:
COPY forum.categories_reloaded
FROM PROGRAM $CODE$
/bin/bash -c 'for i in {1..10}; do echo "$i,Title$i,A generated row"; done' $CODE$
WITH (FORMAT csv);
COPY TO也可以外接程序处理后,再写入文件:
COPY { table_name [ ( column_name [, ...] ) ] | ( query ) }
TO { 'filename' | PROGRAM 'command' | STDOUT }
也可以使用copy元命令:
sampledb=> \?
...
\copy ... perform SQL COPY with data stream to the client host
探索物理备份
推荐pg_basebackup。其他如cp,tar等直接拷贝文件和目录是不推荐的。
PostgreSQL 始终在集群数据目录的 pg_wal/ 子目录中维护一个预写日志 (WAL)。该日志记录了对数据库数据文件所做的所有更改。此日志的主要作用是防止崩溃:如果系统崩溃,可以通过“重放”自上一个检查点以来的日志条目来恢复数据库的一致性。然而,日志的存在使得我们可以可以将文件系统级备份与 WAL 文件备份结合起来以实现数据库备份。如果需要恢复,我们可以恢复文件系统备份,然后从备份的 WAL 文件重放,使系统恢复到当前状态。这种方法的优势在于:
-
我们不需要以完全一致的文件系统备份作为起点。备份中的任何内部不一致都可以通过日志重放来纠正(这与崩溃恢复期间发生的情况没有太大区别)。
-
由于我们可以组合无限长的 WAL 文件序列进行重放,因此只需持续归档 WAL 文件即可实现连续备份(增量备份)。
-
无需将 WAL 条目一直重放至末尾。我们可以随时停止重放,并获得与当时状态一致的数据库快照。因此,该技术支持时间点恢复。
-
如果我们将一系列 WAL 文件持续提供给另一台已加载相同基础备份文件的计算机,我们就拥有了一个热备用系统:我们可以随时启动第二台计算机,它将拥有几乎最新的数据库副本。
💡 物理备份只能做全备,即整个cluster的备份。
💡 本章接下来的实验暂不涉及WAL归档。
执行手动物理备份
先设置备份目录:
sudo mkdir -p /backup/data
sudo chown -R postgres:postgres /backup
sudo chmod -R 700 /backup
执行物理备份,由于有自定义表空间,因此需做表空间目录映射:
$ pg_basebackup -l 'my full backup' -v -D /backup/data -T /data/tbs/ts_a=/backup/tablespaces/ts_a -T /data/tbs/ts_b=/backup/tablespaces/ts_b
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/29000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_15279"
pg_basebackup: write-ahead log end point: 0/29000100
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed
查看备份目录,他几乎就是$PGDATA的拷贝。再加上一个表空间映射目录,就构成了物理备份的全部。
备份目录必须为空,这也意味着同一命令再执行一次就会失败:
$ pg_basebackup -l 'my full backup' -v -D /backup/data -T /data/tbs/ts_a=/backup/tablespaces/ts_a -T /data/tbs/ts_b=/backup/tablespaces/ts_b
pg_basebackup: error: directory "/backup/data" exists but is not empty
在看下pg_basebackup的man page:
pg_basebackup 用于对正在运行的 PostgreSQL 数据库集群进行基础备份。备份不会影响数据库的其他客户端,既可以用于时间点恢复,也可以作为日志传送或流复制备用服务器的起点。
pg_basebackup 会对数据库集群的文件进行精确复制,同时确保服务器自动进入和退出备份模式。备份始终针对整个数据库集群进行;无法备份单个数据库或数据库对象。
对于帮助中的下面两点,我并没有设置,是因为系统自动就满足了。其中max_wal_senders的默认值为10,大于所要求的2。
备份通过使用复制协议的常规 PostgreSQL 连接进行。连接必须使用具有 REPLICATION 权限(参见第 21.2 节)的用户 ID 或超级用户建立,并且 pg_hba.conf 必须允许复制连接。服务器还必须配置 max_wal_senders 足够高,以便为备份提供至少一个 walsender,并为 WAL 流(如果使用)提供一个。
pg verifybackup
$ pg_verifybackup /backup/data/
backup successfully verified
$ echo $?
0
pg_verifybackup 用于检查使用 pg_basebackup 创建的数据库集群备份的完整性,并与服务器在备份时生成的备份清单 (backup_manifest) 进行比对。备份必须以“纯文本”格式存储;“tar”格式的备份可以在提取后进行检查。
需要注意的是,pg_verifybackup 执行的验证并非涵盖运行中的服务器在尝试使用备份时执行的所有检查。即使使用此工具,您仍然应该执行测试还原,并验证生成的数据库是否按预期工作,以及它们是否包含正确的数据。但是,pg_verifybackup 可以检测到许多通常由于存储问题或用户错误而发生的问题。
备份验证分为四个阶段:
- backup_manifest文件存在并有效
- 当前存储在磁盘上的数据文件是否与服务器想要发送的数据文件完全相同
- 对所有文件进行校验和,并将校验和与清单中的值进行比较
- 使用清单来验证恢复备份所需的预写式日志记录是否存在,以及它们是否可以被读取和解析
启动克隆集群
pg_basebackup 是目标集群的完整克隆,包括配置文件。因此启动克隆集群时,可能需要修改一些参数以避免冲突(例如端口冲突)。
$ pg_ctl -D /backup/data/ -o '-p 5433' start
waiting for server to start....2025-06-28 08:15:42.493 UTC [15523] LOG: pgaudit extension initialized
2025-06-28 08:15:42.517 UTC [15523] LOG: redirecting log output to logging collector process
2025-06-28 08:15:42.517 UTC [15523] HINT: Future log output will appear in directory "log".
done
server started
$ psql -p 5433
psql (16.9)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
--------------------+----------+----------+-----------------+-------------+-------------+------------+-----------+-----------------------
demo | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
postgres | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
restoredb | dbadmin | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
sampledb | dbadmin | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
template0 | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
world_temperatures | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
(7 rows)
postgres=# \conninfo
You are connected to database "postgres" as user "postgres" via socket in "/run/postgresql" at port "5433".
$ pg_ctl -D /backup/data/ -o '-p 5433' stop
waiting for server to shut down.... done
server stopped
从物理备份恢复
有时需要用备份覆盖当前的$PGDATA,有时也可以像上例一样,在别处启动cluster,然后从中选择恢复对象。总之,要小心。
作者推荐使用pgBackRest备份解决方案。
PITR 背后的基本概念
PITR是以物理备份为基础,然后不断搬运和应用WAL日志实现的。
当不使用 WAL 归档时,系统通常只创建几个段文件,然后通过将不再需要的段文件重命名为更高的段编号来“回收”它们。系统假设内容在最后一个检查点之前的段文件不再有用,可以回收。
- wal_level:设为replica(默认)或以上
- archive_mode:设为on,默认为off
- archive_command:设为命令将WAL拷贝走,默认为空
验证你的知识
- 物理和逻辑备份的区别?
- pg_dump和pg_dumpall的区别?
- 什么是COPY命令?和
\copy
元命令的区别? - 什么是pg_basebackup命令?
- 什么是时间点恢复 (PITR)?