PG 17 增量备份功能介绍

ops/2024/10/17 23:47:25/

背景

PG 17 新增了增量备份功能,可以通过 pg_basebackup --incremental=PATH_TO_MANIFEST 命令进行增量备份。

  • 官方文档:https://www.postgresql.org/docs/current/app-pgbasebackup.html

在先前版本,其实我们也可以利用 WAL 进行增量备份,逻辑如下:

  1. 某一天对数据库进行一次全量备份,获得一个全量备份集;
  2. 又过了一天,我们将这一天内数据库所有的 WAL 都存起来;
  3. 当我们想恢复数据库时,以全量备份为基础,回放第二步所有的 WAL,最终得到完整的数据集;

功能介绍

wal summarization

wal summarization 是增量备份必备的前置功能,起作用是生成一系列总结文件,记录某个 LSN 区间内,有哪些块被修改了。
在这里插入图片描述

这样增量备份的时候可以通过这些 summarization 文件,找到哪些数据文件发生过变更,增量备份只需要备份这些发生过变更的文件即可。

增量备份

在先前的版本中,我们可以使用 pg_basebackup 功能对数据库进行备份。
而增量备份的大致逻辑如下:

  1. 某一天对数据库进行一次全量备份,获得一个全量备份集;
  2. 又过了一天,我们以上述的全量备份集为基础进行增量备份,得到一个增量备份集 ,这个增量备份的内容就是这一天数据库的变更;
  3. 当我们想恢复数据库时,使用 pg_combinebackup 将全量备份集和增量备份集进行合并,就获得了一个完整的数据集。

优势 & 劣势

优势

可以假想一个场景:用户的大部分操作都是 update 同一行数据。在这种场景下,会生成很多的 WAL,但实际的数据文件变更却很少。在这种场景下,PG 17 的增量备份功能相比原先 redo WAL 的方式就具有非常大的优势。

根据 [2] 中提到的数据,EDB 做过相关的测试:

对一个数据库进行 24h 的 pgbench 测试,初始时数据库只有 3.3GB 大小,测试结束时数据库有 4.3 GB 大小,产生了 77GB 的 WAL 日志。

初始的全量备份集为 3.4GB,后续每 2h 进行一次增量备份,获得的 11 个增量备份集加起来大小只有 3.5GB。
接着,测试人员进行 PITR 测试,要求恢复到指定的时间点。使用原有的 redo WAL 的方式花了 78 分钟,而使用 PG 17 的增量备份只用了 4 分钟,快了十几倍。

劣势

  1. 上述场景的相反状态:如果每次更新都几乎修改了全量数据,那不如直接做全量备份;
  2. 目前不支持输出压缩包,现在增量打出来就是一个文件夹;

使用方法

本章以具体例子说明,PG 17 增量备份的使用方法。

全量备份

启动实例后,首先配置参数 summarize_wal=on 来启用增量备份。灌入一些数据,构造初始数据集

alter system set summarize_wal to on;
select pg_reload_conf();
create table t(a int);
insert into t select generate_series(1,10000);

接着使用 pg_basebackup 命令,拉出一个全量的备份集

pg_basebackup -D /data2/pg/17/inst/full_backup 

可以发现全量备份目录下基本就是数据库需要的文件,其中的 backup_manifest 文件记录了这次全量备份的文件名单。
在这里插入图片描述

增量备份 1

然后我们继续在原实例插入数据:

insert into t select generate_series(10000,20000);

然后使用增量备份功能,–incremental 指定刚刚全量备份的 manifest:

pg_basebackup --incremental=/data2/pg/17/inst/full_backup/backup_manifest -D /data2/pg/17/inst/increment_backup1/

这时候我们进入增量备份的文件目录,其大体结构和全量备份相似,只不过部分文件被替换成了 INCREMENTAL.${ORIGINAL_NAME} 形式
在这里插入图片描述

增量备份 2

然后我们再创建一张表,插入些数据

create table t2(a int);
insert into t2 values(1);
insert into t select generate_series(20001,20005);

进行第二次增量备份,基于第一次增量备份的 manifest 文件

pg_basebackup --incremental=/data2/pg/17/inst/increment_backup1/backup_manifest -D /data2/pg/17/inst/increment_backup2/

合并备份文件

使用 pg_combinebackup 工具进行备份合并,输出到 new_data 文件夹

pg_combinebackup /data2/pg/17/inst/full_backup /data2/pg/17/inst/increment_backup1/ /data2/pg/17/inst/increment_backup2/ -o /data2/pg/17/inst/new_data/

接着我们在新数据库启动实例,查询信息,发现果然已经合并完成:

postgres=# select count(*) from t;count 
-------20006
(1 row)postgres=# select * from t2;a 
---1
(1 row)

PITR 测试

我们构造一种场景:

  1. 向表 t 中插入一条数据,值为 1;
  2. 进行全量备份;
  3. 将表 t 中数据值改成 2;
  4. 将表 t 中数据值再多修改几次;
  5. checkpoint;
  6. 进行增量备份,并将两个备份集合并;

下述所有操作都是全新的数据库,因此 LSN 保持一致。

./initdb -D ../data
echo "archive_mode=on
summarize_wal=on
archive_command = 'cp %p /data2/pg/17/inst/data/archive/%f'" >> ../data/postgresql.conf
pg_ctl start -D ../data -l logfile
psql -c "create table t(a int); insert into t values(1);"
pg_basebackup -D /data2/pg/17/inst/full_backup 
psql -c "update t set a=2 where a=1;"
psql -c "SELECT pg_current_wal_lsn();"
psql -c "update t set a=3 where a=2;"
psql -c "update t set a=4 where a=3;"
psql -c "update t set a=5 where a=4;"
pg_basebackup --incremental=/data2/pg/17/inst/full_backup/backup_manifest -D /data2/pg/17/inst/increment_backup1/
pg_combinebackup /data2/pg/17/inst/full_backup /data2/pg/17/inst/increment_backup1/ -o /data2/pg/17/inst/new_data/

再往 new_data 目录下设置参数:

echo "restore_command = 'cp /data2/pg/17/inst/data/archive/%f %p'
recovery_target_lsn = '0/30000F8'
recovery_target_action=promote
port=5433 " >> ../new_data/postgresql.conf 
touch ../new_data/recovery.signal
pg_ctl start -D ../new_data -l logfile2

可以发现无法恢复到目标 LSN,因为实际上合并后的数据集和 increment_backup1 的 backup_label 相同,因此可以看作是以 increment_backup1 的时间作为 basebackup 开始的时间,而这个 LSN 点位是晚于我们指定的点位的(即 “将表 t 中数据值改成 2” 这个动作的点位),所以是无法对过去点位进行 PITR,这与原先的 pg_basebackup 功能是相同的。
在这里插入图片描述
当然了,如果选取的 LSN/时间 是晚于上述红框中的位点,并且 WAL 日志也都保留着,那就可以进行 PITR。

压测场景

我们边使用 pgbench -i -s 1000 postgres 给上写入压力,同时另开一个终端执行 pg_basebackup --incremental=/data2/pg/17/inst/full_backup/backup_manifest -D /data2/pg/17/inst/increment_backup ,发现增量备份会在压测过程中结束(保留部分压测数据)。

原理分析

参考资料

[1] https://www.postgresql.org/docs/current/app-pgbasebackup.html
[2] https://pganalyze.com/blog/5mins-postgres-17-incremental-backups


http://www.ppmy.cn/ops/126325.html

相关文章

mysql-数据库的操作

目录 认识数据库的基本操作 1、创建数据库 (1)校验集、编码集 (2)指定字符集和校验集创建数据库 2、展示所建立的数据库: 3、查看自己当前数据库: 4、修改数据库 5、删除数据库 6、库的备份与恢复…

java HashMap源码剖析

HashMap 是 Java 集合框架中的一个重要类,它基于哈希表实现,提供了快速的插入、删除和查找操作。 以下是一些关键点: 序列化:HashMap 类实现了 Serializable 接口,这意味着它可以被序列化和反序列化。 初始容量和负载…

日志分析是什么?如何进行日志分析?

日志分析是对诸如计算机系统、网络设备、应用程序等产生的日志文件进行收集、处理、分析和解读的一个过程。这些日志文件记录了系统和应用在运行过程中的各种事件、状态变化、错误信息等详细数据。 通过对这些日志数据的分析,可以深入了解系统的运行情况、发现潜在…

探索Spring Boot在医疗病历B2B交互中的潜力

第2章 设计技术与开发环境 2.1 相关技术介绍 2.1.1 B/S模式分析 C/S模式主要由客户应用程序(Client)、服务器管理程序(Server)和中间件(middleware)三个部件组成。客户应用程序是系统中用户与数据组件交互。服务器程序负责系统资源,如管理信息数据库的有效管理&…

SSM(5)(动态sql <if>、<where>、返回主键值)

返回主键值&#xff1a; 方法一&#xff1a; useGeneratedKeys 为ture 声明 返回主键 keyProperty 表示要返回的值 封装到对象的属性中 但是这一种方法不支持Orcal数据库。 <insert id"save2" parameterType"com.findyou.entity.User" useGenerated…

python从0快速上手(二)IDE选择

在这个代码横飞的世界里&#xff0c;选择一个合适的Python IDE就好比是选择一把顺手的武器。今天&#xff0c;就让我来带你一探究竟&#xff0c;看看市面上有哪些让人眼花缭乱的Python IDE&#xff0c;并一较高下。 1. PyCharm PyCharm&#xff0c;由大名鼎鼎的JetBrains出品…

【C++11】可变模板参数详解

个人主页&#xff1a;chian-ocean 文章专栏 C 可变模板参数详解 1. 引言 C模板是现代C编程中一个非常强大且灵活的工具。在C11标准中&#xff0c;引入了可变模板参数&#xff08;variadic templates&#xff09;&#xff0c;它为模板编程带来了革命性改变。它的出现允许我们…

苍穹外卖学习笔记(二十)

文章目录 用户端历史订单模块&#xff1a;查询历史订单OrderControllerOrderServiceOrderServiceImpl 查询订单详情OrderControllerOrderServiceOrderServiceImpl 用户端历史订单模块&#xff1a; 查询历史订单 OrderController /*** 历史订单*/GetMapping("/historyOrd…