鲲鹏社区首页
中文
注册
开发者
openGauss数据库源码解析系列文章——备份恢复机制:openGauss增量备份技术(下)

openGauss数据库源码解析系列文章——备份恢复机制:openGauss增量备份技术(下)

openGauss

发表于 2025/12/01

0

10.2.2 gs_probackup主要文件

增量备份与全量备份相比,最重要的就是对备份数据和元数据的管理。备份数据主要通过目录来区分,元数据主要通过backup.control文件来管理。gs_probackup的主要目录文件如下。

(1) backup_path:备份根目录,所有实例的备份文件都存放在这个目录下。

(2) backup_path/backups/:数据根目录,所有实例的数据文件都存放在这个目录下。

(3) backup_path/wal/:日志根目录,所有实例的日志文件都存放在这个目录下。

(4) backup_path/backups/instance_name:实例的数据根目录,一个实例的所有数据都存放在这个目录下。实例备份的配置参数文件pg_probackup.conf存放在这个目录下。

(5) backup_path/wal/instance_name:实例的日志根目录,一个实例的所有WAL日志都存放在这个目录下。

(6) backup_path/backups/instance_name/backupID:一个具体备份的数据目录,一次备份的数据存放在这个backupID下,backupID是用这次备份的开始时间start_time进行36位编码生成的,即backupID等于base36enc(backup->start_time),base36enc是一个36位编码的转换函数。备份元数据控制文件backup.control就存放在这个目录下。

gs_probackup备份元数据文件backup.control内容参照pgBackupWriteControl函数写的内容,也可以通过gs_probackup show -B backup-path查看备份元数据信息。

文件内容具体如下:

(1) #Configuration 配置小节。

① backup-mode = "PAGE","PTRACK","DELTA","FULL"。当前只支持FULL和PTRACK。

② stream = true 或者 false,表示是否流复制备份模式。

③ compress-alg = none,zlib或者pglz。

④ compress-level = 0到9,级别越大,压缩率越高。

⑤ from-replica = true或者 false,true表示是从备机备份数据,false表示从主机备份数据。通过执行pg_is_in_recovery SQL函数来判断主备机。

(2) #Compatibility 兼容性小节。

① block-size = 数据库数据文件数据页的大小。

② xlog-block-size = 数据库日志文件日志页的大小。

③ checksum-version = CRC校验算法的版本号,数值类型。global/pg_control文件中的data_checksum_version。

④ program-version = gs_probackup工具版本号,字符串类型。

⑤ server-version = 数据库服务器版本号,字符串类型。

(3) #Result backup info。

① timelineid = 备份时数据库的时间线。

② start-lsn = 备份开始时的XLOG位置。

③ stop-lsn = 备份结束时的XLOG位置。

④ start-time = 备份开始时的时间(备份状态设置为BACKUP_STATUS_RUNNING的时间)。

⑤ merge-time = 备份合并的时间,如果没有合并,则为0。

⑥ end-time = 备份结束时的时间(或者是备份非正常终止的时间)。

⑦ recovery-xid = 这个备份能够恢复到的最早事务ID。

⑧ recovery-time = 这个备份能够恢复到的最早时间点。

⑨ expire-time = 备份过期的时间。

⑩ merge-dest-id = 备份能合并到的备份ID。

⑪ data-bytes = 备份的数据大小,这个是原始大小。

⑫ wal-bytes = 备份的WAL日志文件大小。

⑬ uncompressed-bytes = 备份的数据未压缩大小,不包括WAL文件。

⑭ pgdata-bytes = 备份时数据库的数据目录PGDATA的大小。

⑮ status = 备份状态 "UNKNOWN","OK","ERROR","RUNNING","MERGING","MERGED","DELETING","DELETED","DONE","ORPHAN","CORRUPT"。

⑯ parent-backup-id = 父备份ID,只有在增量备份的时候有效。

⑰ primary_conninfo = 连接数据库的信息。replication,dbname,fallback_application_name,password等连接信息。

⑱ external-dirs = 备份的外部地址列表。

⑲ note = 备份的注释信息。

⑳ content-crc = 整个备份数据的CRC校验值。backup_content.control文件的CRC值。backup_content.control文件存放了备份文件的信息。

10.2.3 gs_probackup备份恢复流程

gs_probackup工具在备份之前需要先进行备份目录初始化,然后进行数据库实例的全量备份,在全量备份的基础上才能进行增量备份。类似地,增量恢复也必须以某个全量备份为基础进行恢复。gs_probackup除了支持本地备份外,也支持通过SSH进行远程备份。下面是几个主要的步骤。

(1) 初始化备份目录。在指定的目录下创建backups/和wal/子目录,分别用于存放备份文件和WAL文件,代码如下:


gs_probackup init -B backup_dir

(2) 初始化一个数据库实例的备份,代码如下:


gs_probackup add-instance -B backup_dir -D data_dir --instance instance_name [remote_options]

(3) 创建指定实例的备份。在进行增量备份之前,必须至少创建一次全量备份。全量还是增量由-b backup_mode参数控制,FULL表示创建全量备份,PTRACK表示创建PTRACK增量备份,代码如下:


gs_probackup backup -B backup_dir --instance instance_name -b backup_mode

(4) 从指定实例的备份中恢复数据,代码如下:


gs_probackup restore -B backup_dir --instance instance_name -i backup_id

如果进行远程备份和恢复,需要初始化备份目录和初始化一个数据库实例的备份,第一步、第二步和本地备份恢复是相同的,执行上文中的(1)和(2)即可。但第三步和第四步需要使用SSH的远程连接信息进行备份和恢复,步骤如下所示。

(1) 远程备份,代码如下:


gs_probackup backup -B backup_dir -b FULL --stream --instance=instance_name --remote-user=remote_user --remote-host=ip --remote-port=remote_port -d db -p port -U user

(2) 远程恢复,代码如下:


gs_probackup restore -B backup_dir --instance=instance_name --remote-user=remote_user --remote-host=ip --remote-port=remote_port --incremental-mode=checksum

下面介绍每个处理函数的详细实现。

(1) init命令的处理函数是do_init,在do_init函数中。

① 首先检查命令行输入的备份路径backup_dir是否为空目录,要求必须为空目录。如果backup_dir不存在,则创建。

② 在backup_dir下创建backups子目录,创建wal子目录。

(2) add-instance的处理函数为do_add_instance,在do_add_instance处理函数中。

① 创建backup_dir/backups/instance_name子目录,创建backup_dir/wal/instance_name子目录。

② 获取实例备份参数信息,包括system_identifier,xlog_seg_size,remote.host,remote.proto,remote.port,remote.path,remote.user,remote.ssh_options,remote.ssh_config等,通过调用函数do_set_config把这些实例备份信息写到backup_path/backups/instance_name/pg_probackup.conf配置文件中。

(3) backup的处理函数为do_backup,do_backup根据参数backup_mode是FULL还是PTRACK进行全量或者增量备份。全量备份和增量备份基本流程是一样的,唯一的区别是全量备份拷贝全部数据文件,增量备份只拷贝自上次备份以来的脏数据页。函数的处理流程如下。

① 调用pgNodeInit初始化备份数据库服务器节点的信息。

② 设置备份状态为BACKUP_STATUS_RUNNING和其他备份元数据,包括备份时间、当前版本、压缩算法、压缩级别和外部目录地址。

③ 调用pgBackupCreateDir创建备份路径,备份路径由pgBackupGetPath构造,路径为backup_dir/backups/instance_name/base36enc(backup->start_time),同时创建外部目录的备份地址,地址为backup_dir/backups/instance_name/ extern_direc。

④ 备份元数据写入到backup.control控制文件中。

⑤ 调用pgdata_basic_setup创建到数据库服务器的连接,检查校验备份工具的block_size、wal_block_size,checksum_version等和数据库服务器的版本是兼容的。调用check_system_identifiers校验系统标识符system_identifier是一致的。检查数据库服务器的ptrack版本是兼容的,并且开关是打开的。

⑥ 调用add_note把备份描述信息添加到备份元数据中。

⑦ 调用do_backup_instance进行数据库实例数据备份。

⑧ 更新备份元数据中的备份结束时间,备份状态为BACKUP_STATUS_DONE。更新备份的留存策略ttl或者expire_time。

⑨ 如果需要校验备份,则调用pgBackupValidate对备份数据进行校验。

⑩ 最后如果需要删除过期的备份或者合并备份,则调用do_retention把过期的备份删除。

(4) restore的处理函数为do_restore_or_validate,函数的处理流程如下。

① 检查如果data目录不为空,看是否为增量恢复,并且当前的增量和路径中存在的数据是否兼容。首先检查两者的system_identifier标识符是否一致,如果不一致,则恢复失败。然后检查目录中是否有备份标签文件,如果存在,则恢复失败。

② 调用catalog_get_backup_list获取实例的所有备份。在catalog_get_backup_list函数中,遍历实例所有的backup.control文件,获取所有backup_id,并且根据backup_id排序。所有增量备份和他们的祖先连接起来,备份元数据有一个parent_backup,指向的就是父备份ID。


/* Link incremental backups with their ancestors.*/
for (i = 0; i < parray_num(backups); i++)
{
pgBackup   *curr = parray_get(backups, i);
pgBackup  **ancestor;
pgBackup key;
if (curr->backup_mode == BACKUP_MODE_FULL)
continue;
key.start_time = curr->parent_backup;  
ancestor = (pgBackup **) parray_bsearch(backups, &key,
pgBackupCompareIdDesc); 
if (ancestor) 
curr->parent_backup_link = *ancestor;
}

③ 在备份列表中,查找满足恢复条件的备份。如果指定了target_backup_id则根据backup_id进行匹配。如果指定了pgRecoveryTarget,则根据pgRecoveryTarget进行匹配,判断函数为satisfy_recovery_target。

④ 如果找到的备份是全量备份,那么当前backup_id就是满足要求的。如果找到的备份是增量备份,需要遍历整个增量备份链表查找之前所有备份都是正常的。

⑤ 检查表空间映射是正常的。

⑥ 如果是INCR_LSN恢复,确定恢复链中的恢复位置。

⑦ 如果恢复前需要验证文件正确性,则调用pgBackupValidate和validate_wal验证数据文件和日志文件正确性。

⑧ 调用restore_chain根据备份链进行恢复。

⑨ 根据传入的恢复参数调用create_recovery_conf创建recovery.conf恢复文件。整个恢复结束。

其他的一些流程处理如下。

(5) help的help_pg_probackup和help_command,这两个函数的处理都在help.cpp中。

① help_pg_probackup命令没有输入参数,就是打印整个工具的命令行使用帮助信息。

② help_command(char *command)有一个输入参数command,根据command参数指定的子命令分别调用相应子命令的帮助处理函数。

(6) del-instance删除实例的处理函数为do_delete_instance,在do_delete_instance处理函数中。

① 调用catalog_get_backup_list获取实例。backup_dir/backups/instance_name子目录下的所有数据备份。遍历删除这些数据备份。

② 删除backup_dir/wal/instance_name子目录下的所有WAL日志文件。

③ 删除实例备份配置文件backup_dir/backups/instance_name/pg_probackup.conf。

④ 删除实例backup_dir/backups/instance_name子目录本身,删除backup_path/wal/instance_name子目录本身。

(7) 设置实例备份配置文件backup_dir/backups/instance_name/pg_probackup.conf的处理函数为do_set_config,do_set_config的根据解析的命令行参数,以name = value的形式把新值设置到配置文件中。pg_probackup.conf是文本文件,为了避免文件写坏,先把内容写到一个pg_probackup.conf.tmp临时文件,最后再重命名为正式文件。

(8) 显示实例备份配置文件backup_dir/backups/instance_name/pg_probackup.conf的处理函数为do_show_config,do_show_config函数根据命令行要求是json还是普通格式把参数以name=value的方式显示出来。

10.2.4 redo日志增量备份恢复流程

在gs_basebackup或者gs_probackup工具全量备份的基础上,再加上数据库的redo日志,就可以实现基于redo日志的增量备份和恢复。如果把所有redo日志都进行归档备份,那么数据库就可以实现基于时间点的恢复PITR,把数据库恢复到基于全量备份以来的任意时间点。当前openGauss没有提供工具进行redo日志的备份,应用可以通过配置归档命令的方式或者自己拷贝的方式把redo日志拷贝到备份目录进行备份。恢复时只需要一个全量备份加上redo日志就可以进行数据库的恢复。这个过程不涉及代码逻辑,所以不再进行详细描述。

10.3 小结

物理备份是通过拷贝文件方式进行的备份,备份文件主要分为数据文件和XLOG文件,为了保证备份可用,需要保证XLOG文件的范围覆盖了备份数据的整个过程。因为在拷贝数据的过程中,这些数据页可能被正在执行的在线事务进行修改,这些修改只能通过XLOG恢复保证数据的一致性。增量备份只拷贝上次备份以来的数据脏页,能减少备份的数据量,提高备份效率,但增量备份只能恢复到备份的某个时间点,无法恢复到任意时间点,任意时间点的恢复只能用全量备份和全量XLOG日志的方式进行实现。