1. 备份原理
1.1 基础备份
基础的备份即全量备份一般分为两种方式,基于物理文件拷贝的备份以及基于快照文件拷贝的备份。
文件备份
备份流程主要有以下几个步骤:
pg_startbackup,执行pg_startbackup后,数据库主要做以下几个事情:
设置Full Page Write
文件备份是热备的方式。因此,在数据库写数据的时候拷贝文件,可能出现拷贝partial write页面数据。所以需要设置Full Page Write,即将checkpoint后第一次修改的数据保存fpi到wal日志,恢复数据的时候就可以检测并修复可能存在的页面partial write问题。
Checkpoint
checkpoint一般分为两种形式:schedule checkpoint / fast checkpoint。生产环境一般使用schedule checkpoint,等待数据库自身做周期性的checkpoint,避免fast checkpoint对数据库造成性能抖动。
生成backup_label
cat /home/postgres/tmp_datadir_polardb_pg_1100_bld/polar_exclusive_backup_label
START WAL LOCATION: 0/4079F768 (file 000000010000000000000001)
CHECKPOINT LOCATION: 0/4079F7A0
BACKUP METHOD: pg_start_backup
BACKUP FROM: master
START TIME: 2023-08-22 12:51:12 UTC
LABEL: test
START TIMELINE: 1backup_label记录了开始进行备份时刻的信息,最重要的两个字段是START WAL LOCATION以及CHECKPOINT LOCATION。START WAL LOCATION为checkpoint开始时记录的位点,CHECKPOINT LOCATION为checkpoint结束时记录的位点,恢复时从START WAL LOCATION位点开始回放数据。
全量拷贝文件。
pg_stopbackup,执行pg_startbackup后,数据库主要做以下几个事情:
退出Full Page Write模式
构造BACKUP END记录并写到wal日志。
拷贝全量备份期间产生的wal日志
快照备份
polardb pg使用pfs文件系统,pfs文件系统以10gb为一个chunk来管理数据。以pfs chunk级别(10gb)读写数据,文件数量少,性能会好一些。
快照备份流程
pg_startbackup
全量拷贝快照数据
异步执行pg_stopbackup
crash recovery方式恢复
由于快照数据不包含BACKUP END wal record,按照备份恢复方式启动,会检测到BACKUP END wal record缺失报错。因此,一般以快照备份的方式进行备份,只能以crash recovery的方式启动,从pg_control文件中读取开始回放日志的位点。
pg_control version number: 1100
Catalog version number: 201809051
Database system identifier: 7278201584508863442
Database cluster state: in production
pg_control last modified: Wed 13 Sep 2023 08:39:39 AM UTC
Latest checkpoint location: 2/611AE4B8
Latest checkpoint's REDO location: 2/611AE480
Latest checkpoint's REDO WAL file: 000000010000000200000001
Latest checkpoint's TimeLineID: 1
Latest checkpoint's PrevTimeLineID: 1
Latest checkpoint's full_page_writes: off
Latest checkpoint's NextXID: 0:10502936
Latest checkpoint's NextOID: 25445
Latest checkpoint's NextMultiXactId: 1
Latest checkpoint's NextMultiOffset: 0
Latest checkpoint's oldestXID: 1236
Latest checkpoint's oldestXID's DB: 1
Latest checkpoint's oldestActiveXID: 10502936
Latest checkpoint's oldestMultiXid: 1
Latest checkpoint's oldestMulti's DB: 1
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Time of latest checkpoint: Wed 13 Sep 2023 08:37:09 AM UTC
Fake LSN counter for unlogged rels: 0/1
Minimum recovery ending location: 0/0
Min recovery ending loc's timeline: 0
Backup start location: 0/0
Backup end location: 0/0
End-of-backup record required: no
wal_level setting: replica
wal_log_hints setting: off
max_connections setting: 1000
max_worker_processes setting: 32
max_prepared_xacts setting: 1000
max_locks_per_xact setting: 64
track_commit_timestamp setting: off
Maximum data alignment: 8
Database block size: 8192
Blocks per segment of large relation: 16777216
WAL block size: 8192
Bytes per WAL segment: 1073741824
Maximum length of identifiers: 64
Maximum columns in an index: 32
Maximum size of a TOAST chunk: 1996
Size of a large-object chunk: 2048
Date/time type storage: 64-bit integers
Float4 argument passing: by value
Float8 argument passing: by value
Data page checksum version: 1
Mock authentication nonce: da9ef26927f88631fc0397066792049e60a534081742b558c7d852df466b8174支持形态
PolarDB-PG一般在具备存储控制权的形态下进行快照备份。
1.2 按时间点恢复 PITR
基本原理

与全量备份集的恢复类似,按时间点恢复也是从backup_label所记录的START WAL LOCATION位点开始回放日志。不同的是,全量备份集回放完所有的wal日志后停止,按时间点恢复回放到指定的target后停止。target一般是时间或者lsn。回放结束后,会切换timeline,并在pg_wal目录产生00000002.history文件。
recovery_target_time = '2021-10-20 14:01:47 UTC'
recovery_target_action = 'promote'日志回收
回放过程中,随着表数据的增大,为了避免耗尽存储空间,需要及时去回放wal日志。回放过程主要是两个进程在工作,startup进程负责回放,bgwriter负责刷脏。db会在checkpoint (restart point)时回收日志,把超过一定阈值的已经回放并且落盘的日志删除。
pg11/pg14区别
恢复时间渲染方式不同,pg11(pg11以下版本)在recovery.conf,pg14(pg12以上版本)在postgresql.conf。
pg14对停止回放做了更强校验,如果回放日志没有覆盖回放的target(如timestamp),pg14会报错(认为日志不完整),pg11则没有任何报错。
1.3 时间线 Timeline
基本原理
timeline用来区分原始的数据库和恢复出来的数据库。

原始数据库集群运行到12:10:00,想恢复到12:05:00,从全量备份集开始回放日志,到12:05:00,切换timeline,以新的数据库集群开始运行。

以wal日志名称前8个字符(32bit)作为timeline。
1.4 增量块级备份
基本原理
对于大的数据库实例,每次都进行全量备份,耗时较长,备份存储占用较多。增量块级备份,只备份相对于上一次全量备份或者增量块级备份,数据库产生变化的block(新增或者更新)。恢复时,基于全量备份和增量块级备份链,合成一个逻辑全量备份。eg:20TB数据库,每天只变化10GB数据,增量块级备份只需备份10GB数据。
增量块级备份几个步骤:
归档wal日志时,同步解析wal日志,记录变化的page到bitmap 索引。
通过wal日志解析获取变化的block:


每次归档wal日志,同步解析wal内容,生成bitmap索引(1bit记录一个block),记录新增/删除或者更新的block。
定期增量块级备份,根据bitmap索引,备份变化的page到增量块级备份集。

每天读取bitmap索引,将变化的page备份到增量块级备份集中,每次备份完成会清空bitmap索引。
恢复时,先恢复全量备份集,再恢复增量块级备份链上的备份集。

1.5 实时日志备份
基本原理
单个wal级别进行归档,最坏情况下会丢失较多的wal日志,RPO指标较差。因此,需要实时的采集wal日志并上传到备份存储,减少灾难场景下恢复数据库的数据丢失。实时日志备份一般分为以下两个步骤。
定期采集wal写入位点,如采用以下sql去查询主库当前数据库写到了具体wal文件的哪个位置。
select * FROM pg_walfile_name_offset(pg_current_wal_flush_lsn())

上传日志,将日志上传到支持append io的备份存储,如oss nfs等。

2. 备份框架
2.1 备份Agent总体框架
背景
PolarDB Backup Agent主要是为了解决以下几个痛点。
提高全量备份效率,解决较大规模数据库备份时间较长问题。
提高日志归档效率,解决单进程日志归档过慢导致日志积压问题。
完善的元数据管理,原生备份工具缺乏有效的元数据管理。
一个易拓展的框架,方便支持更多的备份储存形态,如ext4/dbs/s3/ftp/http/oss ...。
高可用
polardb cluster manager(cm) + polardb backup agent

polardb cluster manager是PolarDB PG高可用的核心组件,下面简称cm。cm里内置backup manager插件,向backup agent发起备份任务,并持久化备份任务。因此,backup agent可以做到无状态,进一步去简化backup agent的处理逻辑,可以支持backup agent进程快速crash recovery。
易拓展
备份Agent内部是一个golang插件运行框架。
插件类型 EXE/DDL
EXE(executable)类型插件,用于运行一个流程,比如全量备份的流程,实现在fullbackup_pipeline插件之中。其中,EXE类型插件会依赖DDL类型插件,比如会依赖pfs插件来提供各种读函数,依赖dbs插件来提供写存储函数。运行时候会生成context(上下文),保存插件运行生命周期内的临时状态。

DLL(Dynamic Link Library)类型插件,EXE插件运行时动态加载,比如各种类型的文件系统、存储系统,会以DDL形式存在。无状态。


其中,存储插件兼容unix文件系统posix语义,类似pg vfs接口。

统一的存储插件接口,易于扩展,同时保持备份主干代码逻辑的简单清晰。
插件运行流程
for {
msg = RecvMsg(srv.notify)
srv.MsgAction(msg)
}接受来自cm的msg,并执行相应的action(异步执行go routine,运行EXE类型插件),比如进行全量备份/恢复,日志备份/恢复。
func (srv *Server) MsgAction(msg *Message) {
plug.module.PluginABI.Init(ctx)
plug.module.PluginABI.Run(ctx)
plug.module.PluginABI.Exit(ctx)
}exe插件每次action,会执行Init(ctx) -> Run(ctx) -> Exit(ctx)。

高性能
横向扩展
备份任务可分布在多个节点同时进行备份。
纵向扩展
单个节点上,支持多个并发进行全量备份、增量日志备份、增量块级备份。
流式压缩解压、加解密、上传下载
压缩解压、加解密、上传下载等过程都在内存中进行,避免引入磁盘IO,影响备份性能。
2.2 与basebackup区别
功能 | polardb backup agent | pg_basebackup |
压缩 | 支持 | 支持 |
并发 | 支持 | 不支持 |
限流 | 支持 | 不支持 |
加密 | 支持 | 不支持 |
元数据 | 支持 | 不支持 |
crc校验 | 支持 | 不支持 |
chunk级别备份 | 支持 | 不支持 |
块级增量备份 | 支持 | 不支持 |
实时日志备份 | 支持 | 不支持 |
跨存储备份恢复 | 支持 | 不支持 |
2.3 开源地址
https://github.com/ApsaraDB/PolarDB-BackupAgent
1. 备份原理
1.1 基础备份
基础的备份即全量备份一般分为两种方式,基于物理文件拷贝的备份以及基于快照文件拷贝的备份。
文件备份
- 备份流程主要有以下几个步骤:
- pg_startbackup,执行pg_startbackup后,数据库主要做以下几个事情:
- 设置Full Page Write
文件备份是热备的方式。因此,在数据库写数据的时候拷贝文件,可能出现拷贝partial write页面数据。所以需要设置Full Page Write,即将checkpoint后第一次修改的数据保存fpi到wal日志,恢复数据的时候就可以检测并修复可能存在的页面partial write问题。
- Checkpoint
checkpoint一般分为两种形式:schedule checkpoint / fast checkpoint。生产环境一般使用schedule checkpoint,等待数据库自身做周期性的checkpoint,避免fast checkpoint对数据库造成性能抖动。
- 生成backup_label
cat /home/postgres/tmp_datadir_polardb_pg_1100_bld/polar_exclusive_backup_label
START WAL LOCATION: 0/4079F768 (file 000000010000000000000001)
CHECKPOINT LOCATION: 0/4079F7A0
BACKUP METHOD: pg_start_backup
BACKUP FROM: master
START TIME: 2023-08-22 12:51:12 UTC
LABEL: test
START TIMELINE: 1backup_label记录了开始进行备份时刻的信息,最重要的两个字段是START WAL LOCATION以及CHECKPOINT LOCATION。START WAL LOCATION为checkpoint开始时记录的位点,CHECKPOINT LOCATION为checkpoint结束时记录的位点,恢复时从START WAL LOCATION位点开始回放数据。
- 全量拷贝文件。
- pg_stopbackup,执行pg_startbackup后,数据库主要做以下几个事情:
- 退出Full Page Write模式
- 构造BACKUP END记录并写到wal日志。
- 拷贝全量备份期间产生的wal日志
快照备份
polardb pg使用pfs文件系统,pfs文件系统以10gb为一个chunk来管理数据。以pfs chunk级别(10gb)读写数据,文件数量少,性能会好一些。
- 快照备份流程
- pg_startbackup
- 全量拷贝快照数据
- 异步执行pg_stopbackup
- crash recovery方式恢复
由于快照数据不包含BACKUP END wal record,按照备份恢复方式启动,会检测到BACKUP END wal record缺失报错。因此,一般以快照备份的方式进行备份,只能以crash recovery的方式启动,从pg_control文件中读取开始回放日志的位点。
pg_control version number: 1100
Catalog version number: 201809051
Database system identifier: 7278201584508863442
Database cluster state: in production
pg_control last modified: Wed 13 Sep 2023 08:39:39 AM UTC
Latest checkpoint location: 2/611AE4B8
Latest checkpoint's REDO location: 2/611AE480
Latest checkpoint's REDO WAL file: 000000010000000200000001
Latest checkpoint's TimeLineID: 1
Latest checkpoint's PrevTimeLineID: 1
Latest checkpoint's full_page_writes: off
Latest checkpoint's NextXID: 0:10502936
Latest checkpoint's NextOID: 25445
Latest checkpoint's NextMultiXactId: 1
Latest checkpoint's NextMultiOffset: 0
Latest checkpoint's oldestXID: 1236
Latest checkpoint's oldestXID's DB: 1
Latest checkpoint's oldestActiveXID: 10502936
Latest checkpoint's oldestMultiXid: 1
Latest checkpoint's oldestMulti's DB: 1
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Time of latest checkpoint: Wed 13 Sep 2023 08:37:09 AM UTC
Fake LSN counter for unlogged rels: 0/1
Minimum recovery ending location: 0/0
Min recovery ending loc's timeline: 0
Backup start location: 0/0
Backup end location: 0/0
End-of-backup record required: no
wal_level setting: replica
wal_log_hints setting: off
max_connections setting: 1000
max_worker_processes setting: 32
max_prepared_xacts setting: 1000
max_locks_per_xact setting: 64
track_commit_timestamp setting: off
Maximum data alignment: 8
Database block size: 8192
Blocks per segment of large relation: 16777216
WAL block size: 8192
Bytes per WAL segment: 1073741824
Maximum length of identifiers: 64
Maximum columns in an index: 32
Maximum size of a TOAST chunk: 1996
Size of a large-object chunk: 2048
Date/time type storage: 64-bit integers
Float4 argument passing: by value
Float8 argument passing: by value
Data page checksum version: 1
Mock authentication nonce: da9ef26927f88631fc0397066792049e60a534081742b558c7d852df466b8174- 支持形态
PolarDB-PG一般在具备存储控制权的形态下进行快照备份。
1.2 按时间点恢复 PITR
基本原理
与全量备份集的恢复类似,按时间点恢复也是从backup_label所记录的START WAL LOCATION位点开始回放日志。不同的是,全量备份集回放完所有的wal日志后停止,按时间点恢复回放到指定的target后停止。target一般是时间或者lsn。回放结束后,会切换timeline,并在pg_wal目录产生00000002.history文件。
recovery_target_time = '2021-10-20 14:01:47 UTC'
recovery_target_action = 'promote'日志回收
回放过程中,随着表数据的增大,为了避免耗尽存储空间,需要及时去回放wal日志。回放过程主要是两个进程在工作,startup进程负责回放,bgwriter负责刷脏。db会在checkpoint (restart point)时回收日志,把超过一定阈值的已经回放并且落盘的日志删除。
pg11/pg14区别
- 恢复时间渲染方式不同,pg11(pg11以下版本)在recovery.conf,pg14(pg12以上版本)在postgresql.conf。
- pg14对停止回放做了更强校验,如果回放日志没有覆盖回放的target(如timestamp),pg14会报错(认为日志不完整),pg11则没有任何报错。
1.3 时间线 Timeline
基本原理
timeline用来区分原始的数据库和恢复出来的数据库。
原始数据库集群运行到12:10:00,想恢复到12:05:00,从全量备份集开始回放日志,到12:05:00,切换timeline,以新的数据库集群开始运行。
以wal日志名称前8个字符(32bit)作为timeline。
1.4 增量块级备份
基本原理
对于大的数据库实例,每次都进行全量备份,耗时较长,备份存储占用较多。增量块级备份,只备份相对于上一次全量备份或者增量块级备份,数据库产生变化的block(新增或者更新)。恢复时,基于全量备份和增量块级备份链,合成一个逻辑全量备份。eg:20TB数据库,每天只变化10GB数据,增量块级备份只需备份10GB数据。
增量块级备份几个步骤:
- 归档wal日志时,同步解析wal日志,记录变化的page到bitmap 索引。
通过wal日志解析获取变化的block:
每次归档wal日志,同步解析wal内容,生成bitmap索引(1bit记录一个block),记录新增/删除或者更新的block。
- 定期增量块级备份,根据bitmap索引,备份变化的page到增量块级备份集。
每天读取bitmap索引,将变化的page备份到增量块级备份集中,每次备份完成会清空bitmap索引。
- 恢复时,先恢复全量备份集,再恢复增量块级备份链上的备份集。
1.5 实时日志备份
基本原理
单个wal级别进行归档,最坏情况下会丢失较多的wal日志,RPO指标较差。因此,需要实时的采集wal日志并上传到备份存储,减少灾难场景下恢复数据库的数据丢失。实时日志备份一般分为以下两个步骤。
- 定期采集wal写入位点,如采用以下sql去查询主库当前数据库写到了具体wal文件的哪个位置。
select * FROM pg_walfile_name_offset(pg_current_wal_flush_lsn())
- 上传日志,将日志上传到支持append io的备份存储,如oss nfs等。
2. 备份框架
2.1 备份Agent总体框架
背景
PolarDB Backup Agent主要是为了解决以下几个痛点。
- 提高全量备份效率,解决较大规模数据库备份时间较长问题。
- 提高日志归档效率,解决单进程日志归档过慢导致日志积压问题。
- 完善的元数据管理,原生备份工具缺乏有效的元数据管理。
- 一个易拓展的框架,方便支持更多的备份储存形态,如ext4/dbs/s3/ftp/http/oss ...。
高可用
- polardb cluster manager(cm) + polardb backup agent
polardb cluster manager是PolarDB PG高可用的核心组件,下面简称cm。cm里内置backup manager插件,向backup agent发起备份任务,并持久化备份任务。因此,backup agent可以做到无状态,进一步去简化backup agent的处理逻辑,可以支持backup agent进程快速crash recovery。
易拓展
备份Agent内部是一个golang插件运行框架。
- 插件类型 EXE/DDL
- EXE(executable)类型插件,用于运行一个流程,比如全量备份的流程,实现在fullbackup_pipeline插件之中。其中,EXE类型插件会依赖DDL类型插件,比如会依赖pfs插件来提供各种读函数,依赖dbs插件来提供写存储函数。运行时候会生成context(上下文),保存插件运行生命周期内的临时状态。
- DLL(Dynamic Link Library)类型插件,EXE插件运行时动态加载,比如各种类型的文件系统、存储系统,会以DDL形式存在。无状态。
其中,存储插件兼容unix文件系统posix语义,类似pg vfs接口。
统一的存储插件接口,易于扩展,同时保持备份主干代码逻辑的简单清晰。
- 插件运行流程
for {
msg = RecvMsg(srv.notify)
srv.MsgAction(msg)
}接受来自cm的msg,并执行相应的action(异步执行go routine,运行EXE类型插件),比如进行全量备份/恢复,日志备份/恢复。
func (srv *Server) MsgAction(msg *Message) {
plug.module.PluginABI.Init(ctx)
plug.module.PluginABI.Run(ctx)
plug.module.PluginABI.Exit(ctx)
}exe插件每次action,会执行Init(ctx) -> Run(ctx) -> Exit(ctx)。
高性能
- 横向扩展
备份任务可分布在多个节点同时进行备份。
- 纵向扩展
单个节点上,支持多个并发进行全量备份、增量日志备份、增量块级备份。
- 流式压缩解压、加解密、上传下载
压缩解压、加解密、上传下载等过程都在内存中进行,避免引入磁盘IO,影响备份性能。
2.2 与basebackup区别
功能 | polardb backup agent | pg_basebackup |
压缩 | 支持 | 支持 |
并发 | 支持 | 不支持 |
限流 | 支持 | 不支持 |
加密 | 支持 | 不支持 |
元数据 | 支持 | 不支持 |
crc校验 | 支持 | 不支持 |
chunk级别备份 | 支持 | 不支持 |
块级增量备份 | 支持 | 不支持 |
实时日志备份 | 支持 | 不支持 |
跨存储备份恢复 | 支持 | 不支持 |




