暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB-PG备份体系概述

内核开发者 2023-10-17
759

1. 备份原理

1.1 基础备份

基础的备份即全量备份一般分为两种方式,基于物理文件拷贝的备份以及基于快照文件拷贝的备份。

文件备份

  • 备份流程主要有以下几个步骤:

  1. pg_startbackup,执行pg_startbackup后,数据库主要做以下几个事情:

  1. 设置Full Page Write

文件备份是热备的方式。因此,在数据库写数据的时候拷贝文件,可能出现拷贝partial write页面数据。所以需要设置Full Page Write,即将checkpoint后第一次修改的数据保存fpi到wal日志,恢复数据的时候就可以检测并修复可能存在的页面partial write问题。

  1. Checkpoint

checkpoint一般分为两种形式:schedule checkpoint / fast checkpoint。生产环境一般使用schedule checkpoint,等待数据库自身做周期性的checkpoint,避免fast checkpoint对数据库造成性能抖动。

  1. 生成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: 1

backup_label记录了开始进行备份时刻的信息,最重要的两个字段是START WAL LOCATION以及CHECKPOINT LOCATION。START WAL LOCATION为checkpoint开始时记录的位点,CHECKPOINT LOCATION为checkpoint结束时记录的位点,恢复时从START WAL LOCATION位点开始回放数据。

  1. 全量拷贝文件。

  2. pg_stopbackup,执行pg_startbackup后,数据库主要做以下几个事情:

    1. 退出Full Page Write模式

    2. 构造BACKUP END记录并写到wal日志。

  3. 拷贝全量备份期间产生的wal日志

快照备份

polardb pg使用pfs文件系统,pfs文件系统以10gb为一个chunk来管理数据。以pfs chunk级别(10gb)读写数据,文件数量少,性能会好一些。

  • 快照备份流程

  1. pg_startbackup

  2. 全量拷贝快照数据

  3. 异步执行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

  1. EXE(executable)类型插件,用于运行一个流程,比如全量备份的流程,实现在fullbackup_pipeline插件之中。其中,EXE类型插件会依赖DDL类型插件,比如会依赖pfs插件来提供各种读函数,依赖dbs插件来提供写存储函数。运行时候会生成context(上下文),保存插件运行生命周期内的临时状态。

  1. 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 基础备份

基础的备份即全量备份一般分为两种方式,基于物理文件拷贝的备份以及基于快照文件拷贝的备份。

文件备份

  • 备份流程主要有以下几个步骤:
  1. pg_startbackup,执行pg_startbackup后,数据库主要做以下几个事情:
  1. 设置Full Page Write

文件备份是热备的方式。因此,在数据库写数据的时候拷贝文件,可能出现拷贝partial write页面数据。所以需要设置Full Page Write,即将checkpoint后第一次修改的数据保存fpi到wal日志,恢复数据的时候就可以检测并修复可能存在的页面partial write问题。

  1. Checkpoint

checkpoint一般分为两种形式:schedule checkpoint / fast checkpoint。生产环境一般使用schedule checkpoint,等待数据库自身做周期性的checkpoint,避免fast checkpoint对数据库造成性能抖动。

  1. 生成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: 1

backup_label记录了开始进行备份时刻的信息,最重要的两个字段是START WAL LOCATION以及CHECKPOINT LOCATION。START WAL LOCATION为checkpoint开始时记录的位点,CHECKPOINT LOCATION为checkpoint结束时记录的位点,恢复时从START WAL LOCATION位点开始回放数据。

  1. 全量拷贝文件。
  2. pg_stopbackup,执行pg_startbackup后,数据库主要做以下几个事情:
    1. 退出Full Page Write模式
    2. 构造BACKUP END记录并写到wal日志。
  3. 拷贝全量备份期间产生的wal日志

快照备份

polardb pg使用pfs文件系统,pfs文件系统以10gb为一个chunk来管理数据。以pfs chunk级别(10gb)读写数据,文件数量少,性能会好一些。

  • 快照备份流程
  1. pg_startbackup
  2. 全量拷贝快照数据
  3. 异步执行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
  1. EXE(executable)类型插件,用于运行一个流程,比如全量备份的流程,实现在fullbackup_pipeline插件之中。其中,EXE类型插件会依赖DDL类型插件,比如会依赖pfs插件来提供各种读函数,依赖dbs插件来提供写存储函数。运行时候会生成context(上下文),保存插件运行生命周期内的临时状态。

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

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论