👋 我是三笠 ,一名专注数据库运维与架构的 DBA,平时分享数据库实战经验与技术笔记,个人网站:ora100.com
前言
最近搭了一套金仓数据库 KingbaseES V9R4C19(SQL Server 兼容版) 的测试环境,准备研究一下它的备份恢复能力。
平时 Oracle 的 RMAN 用得比较多,换成 KingbaseES,我最先想了解的也是这块。毕竟数据库安装部署只是第一步,真正到了日常运维阶段,备份恢复才是绕不开的事情。
这次直接在自己的 ESXi 环境里新建了两台银河麒麟 V10 虚拟机,一台作为源数据库,另一台专门用于恢复验证。
备份工具使用 sys_rman,另外还用到了 ktrack,主要是为了测试块级增量备份。
整个测试涵盖了全量备份、文件级增量、块级增量、增量链恢复,以及 PITR 时间点恢复。
前面几项跑通之后,我又针对 PITR 做了两组对照:
- 不指定备份集,让
sys_rman根据目标时间自动选择。 - 手动指定较早的全量备份,恢复到同一个时间点。
主要想看看,不同的备份起点会对 WAL 回放产生什么影响,以及最终恢复的数据是否一致。
实际测试下来,两种方式都顺利完成了恢复,而自动选择备份集的过程也有一些值得关注的地方。
下面就按操作顺序,把这次备份恢复过程整理一下。
先把测试环境准备好
我在 VMware ESXi 上准备了两台独立的虚拟机。
源端负责生成测试数据、执行备份,恢复端负责接收备份文件并验证恢复结果。
具体环境如下:
| 项目 | 配置 |
|---|---|
| 虚拟化平台 | VMware ESXi 7.0.3 |
| 操作系统 | 银河麒麟 V10 SP3 |
| 数据库 | KingbaseES V9 |
| 数据库版本 | V009R004C019 |
| sys_rman | 2.27 |
| ktrack | 2.2 |
| CPU / 内存 | 每台 4 vCPU / 8 GiB |
| 数据库块大小 | 8 KB |
磁盘方面,源端分配了 80 GiB 数据盘和 100 GiB 备份盘,恢复端使用独立的 100 GiB 磁盘。
备份在源端生成后,再复制到恢复端进行验证。整个恢复过程都在独立环境中完成,不会覆盖源数据库。
恢复端还关闭了归档,并使用独立的恢复配置和目录,避免恢复过程中产生的文件影响原有备份仓库。
这样一来,备份、传输、恢复和数据校验几个环节就分开了,后续操作也更方便。
全量和增量备份,先跑一遍
KingbaseES 提供了 sys_rman 备份恢复工具。
虽然名字和 Oracle RMAN 很接近,但实际使用时,备份集管理、参数配置以及增量备份的实现方式都有自己的特点,还是需要熟悉一下。
这次主要验证了三种备份类型:
| 备份类型 | 参数 | 说明 |
|---|---|---|
| 全量备份 | full |
生成完整的基础备份 |
| 文件级增量 | incr |
按文件级增量机制生成备份 |
| 块级增量 | page |
结合块变化跟踪进行备份 |
先从全量备份开始。
备份完成后,将备份文件复制到恢复端,再通过 sys_rman 执行恢复,检查数据库能否正常启动、测试表是否完整。
这一部分比较顺利。
随后继续测试文件级增量和块级增量。
不过,块级增量备份这里遇到了一个需要注意的配置问题。
为什么执行了 page 备份,结果却是 incr?
第一次执行 page 类型备份时,命令正常结束,但检查备份集后发现,实际生成的类型是 incr,并不是预期的 page。
于是回头检查了 ktrack 的配置。
ktrack 是这次块级增量备份使用的变化块跟踪组件。检查发现,当时没有正确启用块跟踪功能,因此这次备份没有按预期生成 page 类型备份。
调整配置后,将 ktrack.enable 设置为 1,重新建立全量备份基线,再执行初始化:
sql
SELECT ktrack_init_lsn();
初始化前,返回的 LSN 为:
text
0/0
初始化后变成:
text
0/14000028
随后重新执行块级增量备份,再检查备份集,类型已经是 page。
这一步虽然多花了一点时间,但也提醒了我一件事:
备份命令执行成功,不代表实际生成的备份类型就一定符合预期。
尤其是涉及 ktrack 这类组件时,除了检查任务是否成功,还应该确认相关配置和备份集信息。
增量备份链能不能正常恢复?
块级增量备份完成后,我又继续生成了后续增量备份,形成一条完整的备份链。
结构大致如下:
text
Full Backup
|
+-- Page Incremental 1
|
+-- Page Incremental 2
然后在恢复端分别验证全量备份和增量链恢复。
全量备份恢复后,测试表共有 65,536 行数据,金额合计为:
text
294671073.92
继续验证增量备份链,恢复到第三阶段后,数据变成 65,509 行,金额合计为:
text
294673604.87
除了记录数和金额合计,我还对测试表的完整数据及表结构进行了校验,结果均与对应阶段的参考数据一致。
到这里,全量备份、文件级增量和块级增量的备份恢复验证基本完成。
接下来,重点看看 sys_rman 的 PITR 时间点恢复能力。
PITR 能不能准确恢复到指定时间?
PITR 是 Point-in-Time Recovery,也就是时间点恢复。
它的原理并不复杂:先恢复一份可用的备份,再通过 WAL 日志回放,将数据库恢复到指定的历史时间点。
不过,数据库能够启动,并不代表数据就一定恢复到了预期状态。
为了验证这一点,我专门准备了几组不同阶段的数据。
测试使用两张表:
text
br_lab.items
br_lab.markers
其中,items 存放模拟业务数据,包括主键、文本和金额等字段;markers 用来记录不同的数据变化阶段。
初始写入 65,536 行数据。
随后分阶段执行更新、插入和删除操作,并在事务中写入对应的阶段标记。
最终形成四组不同的数据状态:
| 数据阶段 | 记录数 | 金额合计 |
|---|---|---|
| 初始数据 | 65,536 | 294671073.92 |
| 第一阶段 | 65,527 | 294671853.57 |
| 第二阶段 | 65,518 | 294672697.22 |
| 第三阶段 | 65,509 | 294673604.87 |
这次 PITR 的目标是:
恢复到第二阶段,保留之前已经提交的数据变更,但不包含第三阶段的事务。
指定的恢复时间为:
text
2026-11-11 04:58:35.766296 UTC
这里使用的是 UTC 时间,换算成北京时间为 12:58:35。
而第三阶段事务的实际提交时间为:
text
2026-11-11 05:00:31.914027 UTC
也就是北京时间 13:00:31。
第三阶段事务提交时间晚于 PITR 目标时间,因此不应该出现在恢复结果中。
按照预先保存的数据状态,正确的恢复结果应该是:
text
items 行数:65518
amount 合计:294672697.22
marker_id:1、2、3
第三阶段对应的 marker 4 不应该出现。
为了避免只凭行数判断,我还保存了各阶段的参考数据,后面会通过完整数据导出和 SHA-256 校验进一步确认。
不指定备份集,让 sys_rman 自动选择
开始 PITR 测试时,备份仓库中已经存在多份全量和增量备份。
部分备份集如下:
text
20261111-045035F
20261111-045035F_20261111-045523I
20261111-045656F
20261111-045656F_20261111-045751P
20261111-045656F_20261111-050033P
20261111-050035F
从备份集名称可以看出,仓库中同时存在全量、文件级增量和块级增量备份。
这次我没有手动指定具体备份集,而是直接告诉 sys_rman 要恢复到哪个时间点。
执行命令如下,路径和变量名称已做简化:
bash
"${KES_BIN}/sys_rman" \
--config="${LAB_ROOT}/pitr_auto.conf" \
--stanza="${STANZA}" \
--archive-mode=off \
--type=time \
--target='2026-11-11 04:58:35.766296+00' \
--target-action=pause \
restore
其中:
--type=time:按指定时间点恢复。--target:指定 PITR 目标时间。--target-action=pause:到达目标恢复位置后暂停。--archive-mode=off:关闭恢复端的归档模式。
这里没有使用 --set 参数,因此由 sys_rman 自动选择备份集。
执行后,日志显示:
text
repo1: restore backup set
20261111-045656F_20261111-045751P
recovery will start at
2026-11-11 04:57:51
可以看到,sys_rman 最终选择的是:
text
20261111-045656F_20261111-045751P
也就是 04:57:51 对应的块级增量备份。
仓库中虽然还有更新的备份,但其时间已经晚于本次指定的 PITR 目标。
从这次选择结果来看,工具能够根据目标时间找到适合的备份起点,而不需要我手动指定备份集。
接下来就是 WAL 回放。
恢复日志中的关键内容如下:
text
starting point-in-time recovery to
2026-11-11 04:58:35.766296+00
restored log file "000000010000000000000016"
redo starts at 0/16000028
restored log file "000000010000000000000017"
recovery stopping before commit of transaction 1709,
time 2026-11-11 05:00:31.914027+00
recovery has paused
这段日志里有两个比较关键的信息。
首先,REDO 从 0/16000028 开始,恢复过程中获取了两个 WAL 段:
text
000000010000000000000016
000000010000000000000017
其次,数据库在事务 1709 提交之前停止了 WAL 回放。
这个事务的提交时间是 05:00:31.914027,晚于指定的恢复目标 04:58:35.766296。
因此,该事务没有被应用到恢复结果中。
从日志来看,恢复过程符合预期。不过,最终数据是否正确,还需要进一步验证。
恢复完成后,数据对不对?
看到日志中的:
text
recovery has paused
说明数据库已经按照指定动作暂停恢复。
我先查询数据库状态:
sql
SELECT
sys_is_in_recovery(),
sys_is_wal_replay_paused();
返回结果:
text
sys_is_in_recovery | sys_is_wal_replay_paused
-------------------+--------------------------
t | t
两个值都是 t。
说明数据库仍然处于恢复状态,并且 WAL 回放已经暂停。
接着检查测试表:
sql
SELECT
COUNT(*) AS row_count,
SUM(amount) AS total_amount
FROM br_lab.items;
SELECT marker_id
FROM br_lab.markers
ORDER BY marker_id;
结果如下:
text
row_count : 65518
total_amount : 294672697.22
marker_id
---------
1
2
3
与第二阶段的数据完全一致。
第三阶段对应的 marker 4 没有出现。
但只检查行数和金额合计还不够。
因为即使记录数和汇总金额相同,也不能完全排除某些记录内容存在差异。
所以我又将两张测试表的完整数据按主键排序导出为 CSV,分别计算 SHA-256,并与第二阶段保存的参考结果进行比较。
最终,两张表的完整数据哈希一致,表结构核对也一致。
这说明在本次测试的数据范围内,PITR 恢复结果与预期的第二阶段状态一致。
第一次 PITR 验证通过。
换成旧全量备份,再恢复一次
第一次恢复由 sys_rman 自动选择备份集,使用的是块级增量备份。
接下来,我又做了一次对照。
这次不让工具自动选择,而是明确指定较早的全量备份,看看恢复路径会有什么变化。
重新准备独立的恢复目录后,执行:
bash
"${KES_BIN}/sys_rman" \
--config="${LAB_ROOT}/pitr_full.conf" \
--stanza="${STANZA}" \
--archive-mode=off \
--set=20261111-045656F \
--type=time \
--target='2026-11-11 04:58:35.766296+00' \
--target-action=pause \
restore
与第一次相比,主要区别就是增加了:
bash
--set=20261111-045656F
这次明确指定 04:56:56 对应的全量备份作为恢复起点。
恢复日志显示:
text
repo1: restore backup set 20261111-045656F
recovery will start at
2026-11-11 04:56:56
redo starts at 0/14000028
可以看到,这次 REDO 起始位置变成了:
text
0/14000028
相比第一次的 0/16000028,起始位置更早。
对应获取的 WAL 段也增加到了四个:
text
000000010000000000000014
000000010000000000000015
000000010000000000000016
000000010000000000000017
继续查看恢复日志:
text
recovery stopping before commit of transaction 1709,
time 2026-11-11 05:00:31.914027+00
recovery has paused
与第一次相同,数据库仍然在事务 1709 提交之前暂停。
随后检查恢复状态:
text
sys_is_in_recovery() = t
sys_is_wal_replay_paused() = t
再核对测试数据:
text
items 行数:65518
amount 合计:294672697.22
marker_id:1、2、3
最后重新导出两张表的完整数据,进行 SHA-256 和结构校验。
结果同样与第二阶段的参考数据一致。
第二次 PITR 也顺利通过。
两条恢复路径,究竟有什么区别?
两次恢复使用相同的目标时间,最终数据也完全一致。
不过,恢复过程中使用的备份起点和 WAL 并不相同。
整理一下:
| 对比项目 | 自动选择备份 | 指定旧全量 |
|---|---|---|
| 恢复起点 | 块级增量备份 | 全量备份 |
| REDO 起始 LSN | 0/16000028 | 0/14000028 |
| 获取 WAL 段数 | 2 | 4 |
| WAL 回放停止位置 | 事务 1709 提交前 | 事务 1709 提交前 |
| 恢复状态 | 已暂停 | 已暂停 |
| 恢复数据阶段 | 第二阶段 | 第二阶段 |
| 完整数据校验 | 一致 | 一致 |
区别主要体现在恢复起点。
自动选择备份时,sys_rman 使用了距离目标时间更近的块级增量备份,REDO 从 0/16000028 开始。
手动指定旧全量备份时,REDO 从 0/14000028 开始,需要获取更多 WAL。
这次两条路径分别获取了 2 段和 4 段 WAL。
需要说明的是,这里比较的是恢复过程中获取的 WAL 段数,并不是恢复耗时。
WAL 段数更少,不代表恢复速度一定更快。实际恢复时间还与备份体积、存储性能和日志回放量等因素有关。
这次没有专门进行 RTO 性能测试,因此不对恢复速度作结论。
不过,从操作体验来看,sys_rman 自动选择备份集还是比较方便的。
如果备份仓库里有多份全量和增量备份,手动选择恢复起点需要先确认备份链和时间范围。
而这次只需要指定目标时间,工具就选择了对应的块级增量备份,随后完成 WAL 回放。
更重要的是,两条不同的恢复路径最终都通过了数据校验。
这次测试,有几个地方值得记下来
整个流程跑完后,我又回头整理了一下操作记录。
首先,备份成功之后,还要检查备份集。
这次测试块级增量时,第一次执行 page 备份,实际生成的却是 incr。调整 ktrack 配置后,才成功生成预期的块级增量备份。
所以不能只看命令是否成功,还要确认备份类型、依赖关系以及备份集状态。
其次,PITR 的恢复起点不一定只有一个。
这次使用同一个目标时间,分别从块级增量备份和较早的全量备份开始恢复。
虽然 REDO 起始位置和获取的 WAL 段数不同,但最终都恢复到了相同的数据状态。
最后,恢复成功不等于验证完成。
这次除了查看 sys_rman 日志,我还检查了数据库恢复状态、事务停止位置、测试表记录数、金额合计、阶段标记以及完整数据哈希。
这些检查结合起来,才能更有把握地判断恢复结果是否符合预期。
当然,PITR 能否顺利完成,还依赖可用的基础备份和连续的 WAL 日志。
日常运维中,除了关注备份任务是否成功,也要检查备份链和归档日志是否完整,并定期进行恢复演练。
毕竟,备份策略配置得再完善,如果从来没有实际恢复过,真正需要使用时还是存在不确定性。
最后
这次围绕 KingbaseES V9 的 sys_rman 和 ktrack,把全量备份、文件级增量、块级增量、增量链恢复以及 PITR 时间点恢复都测试了一遍。
整体过程比较顺利。
其中让我印象比较深的是 PITR 自动选择备份集的过程。
同样恢复到指定时间点,自动选择的块级增量路径获取了 2 段 WAL,而手动指定旧全量备份的路径获取了 4 段 WAL。两条路径最终都在相同的事务提交之前暂停,恢复后的测试数据也完全一致。
对于日常数据库运维来说,能够根据目标时间自动选择合适的备份起点,确实能减少一些手动判断和操作。
不过,工具能完成备份恢复,最终的数据验证仍然需要 DBA 自己把关。
备份成功只是第一步,真正把数据恢复出来,并确认恢复结果符合预期,才算完成了一次备份恢复演练。
这次算是把 KingbaseES 的备份恢复流程摸了一遍,后面有机会再继续折腾其他运维功能。
写文章之外,我也在持续完善自己的数据库学习与运维平台 ORA100。
平时整理的数据库实战案例、常用 SQL、运维脚本和实用工具,都可以在这里找到。
网站地址:ora100.com
感兴趣的话,可以来看看。
