金仓数据库物理备份实战:sys_rman 全流程演练与误覆盖抢救

10 阅读11分钟

干运维这行,逻辑备份和物理备份我都用,但说句心里话,真正能在生产环境里扛事的,还得是物理备份。逻辑备份导出的是 SQL 语句,数据量一大就慢得让人抓狂,而且没法做时间点恢复。物理备份不一样,它直接拷数据文件,配上 WAL 归档日志,想恢复到过去哪一秒都行。

这篇文章我想把金仓数据库(KingbaseES)的物理备份彻底讲明白,核心工具就是 sys_rman。我会从环境准备一路演练到全量、差异、增量备份,再到模拟误删之后怎么救回来,最后还会聊一个特别揪心的真实场景:有人手滑用旧备份把生产库覆盖了,这种时候该怎么抢救。内容都是实操验证过的,版本基于 V9R3C18。

一、物理备份和逻辑备份,到底差在哪

先把概念厘清。sys_rman 是金仓提供的物理备份恢复工具,它干的事是把数据库的数据文件做物理拷贝,同时归档 WAL(Write-Ahead Logging)日志。靠着这两样东西,它支持全量备份、差异备份和文件级增量备份三种策略,还能做基于时间点的恢复,也就是我们常说的 PITR(Point-In-Time Recovery)。

PITR 是物理备份最值钱的能力。它意味着你可以把数据库精确地拉回到过去任意一个历史时刻,这在应对误操作时几乎是救命稻草。后面的实战我会专门演示这一点。

二、动手前的环境准备

开始之前,有几样东西得确认好。我的实验环境用的是 MySQL 兼容模式实例,环境变量是这么配的:

cat >> /home/kingbase/.bashrc << EOF
export KINGBASE_DATABASE=test
export KINGBASE_USER=system
export KINGBASE_HOME=/KingbaseES/V9/Server
export KINGBASE_DATA=/KingbaseES/kes_shanjia
export PATH=\$KINGBASE_HOME/bin:\$PATH
export KINGBASE_PORT=54321
EOF

接着确认两件事。第一,数据库实例正常运行:

ksql -d kingbase -U system -p 54321 -c "SELECT sys_is_in_recovery();"

第二,也是物理备份的前提条件,必须开启归档模式。这一步千万不能跳过,因为没有归档日志,PITR 就无从谈起:

ksql -Usystem -dtest -c "show archive_mode;show archive_command"

三、用 sys_backup.sh 初始化备份环境

sys_rman 的备份环境不是手动一点点配的,金仓提供了 sys_backup.sh 脚本帮你一键初始化。但脚本背后的配置文件 sys_backup.conf 才是关键,这里的参数决定了你整套备份策略长什么样。我把几个核心参数挑出来说说:

_target_db_style="single"          # 单机环境,集群另说
_one_db_ip="192.168.40.128"        # 要备份的主机 IP
_repo_ip="192.168.40.128"          # 备份文件存放位置的 IP
_stanza_name="kingbase"            # 标签名,后面命令都要用到
_os_user_name="kingbase"           # 运行 kes 的 OS 用户
_repo_path="/data/kesbackup/rman"  # 备份文件和归档日志的存放目录
_repo_retention_full_count=5       # 备份集保留策略,保留 5 份
_crond_full_days=7                 # 每 7 天做一次全量
_crond_diff_days=0                 # 0 表示不做差异备份
_crond_incr_days=1                 # 每天做一次增量
_single_data_dir="/KingbaseES/kes_shanjia"   # 数据库目录路径
_single_bin_dir="/KingbaseES/V9/Server/bin"  # 数据库 bin 目录
_single_db_port="54321"            # 监听端口
_compress_type=none                # 压缩类型

这里我想多嘴一句 _stanza_name。stanza 这个概念在 sys_rman 里贯穿始终,它本质上是一个备份配置单元的标签,后面几乎每条命令都要带 --stanza=kingbase。配置改好后执行初始化:

sys_backup.sh init

四、全量备份与恢复演练

环境就绪,先做一次全量备份。全量是一切的基础,差异和增量都得依附在它上面:

sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase --type=full backup

备份完用 info 命令看一眼备份集信息,确认成功:

sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase info

接下来模拟一场灾难。先建表插数据,记下当前时间,这个时间点很重要,后面恢复要靠它:

CREATE TABLE test_backup(id int, name varchar(50));
INSERT INTO test_backup VALUES(1, 'test_full_backup');
SELECT now();

然后手滑,把表删了:

DROP TABLE test_backup;

恢复流程是这样的。先停库,把原数据目录改名留底(注意不是删,是重命名,给自己留条后路):

sys_ctl stop
mv /KingbaseES/kes_shanjia /KingbaseES/kes_shanjia.bak

执行还原:

sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase restore

五、PITR 的"暂停"机制:这是安全设计,不是 Bug

启动数据库后,你去翻日志,会看到一段让初学者很困惑的输出:

LOG:  在0/14009788上已到达一致性恢复状态
LOG:  恢复停止在事务 950 提交之前, 时间 2026-06-26 01:44:05.506245+08
LOG:  恢复操作已暂停
HINT:  执行sys_wal_replay_resume()继续。
LOG:  数据库系统准备接受只读请求的连接

数据库恢复到了目标时间点,但它没有直接进入读写状态,而是停在只读模式,提示你"恢复操作已暂停"。我第一次见这个的时候也愣了一下,以为出错了。

后来才明白,这恰恰是金仓设计上的精巧之处。这个暂停窗口,是把最后的审查权交给了 DBA。你可以先在只读模式下慢慢查数据,确认恢复的时间点对不对、数据是不是你要的那个状态,确认无误了再提交。这相当于在数据真正"落盘"前给你一道保险,防止因为恢复点选错而造成二次数据丢失。

确认没问题后,执行这条命令完成最终提权,数据库才会进入读写模式:

SELECT sys_wal_replay_resume();

恢复完别忘了善后。kingbase.auto.conf 里那些还原参数得清理掉,否则下次启动会出岔子:

vi /KingbaseES/kes_shanjia/kingbase.auto.conf
# 删除 restore_command、recovery_target_time、
# recovery_target_action 以及其他 recovery_target_* 参数

清理后重启,再做一次新的全量备份,让一切回归正轨:

sys_ctl restart
sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase --type=full backup

六、差异备份与增量备份:理解"备份链"

全量讲完,该聊差异和增量了。这两者的区别和依赖关系,是物理备份里最需要建立直觉的部分。

先做一个全量作为基础,记下它的备份集名称,比如 20260626-132418F,后面合并要用:

sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase --type=full backup

插一条新数据,做差异备份。差异备份记录的是相对于上一次全量的变化,所以它的体积明显比全量小:

INSERT INTO test_backup VALUES(2, 'after_full_backup_diff');
sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase --type=diff backup

再插一条,做文件增量备份。增量备份的 backup reference list 会指向它依赖的全量备份集:

INSERT INTO test_backup VALUES(3, 'after_diff_backup_incr');
sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase --type=incr backup

现在到了关键的验证环节。删表,清空数据目录,然后还原(默认还原最新备份集):

sys_ctl stop
rm -rf /KingbaseES/kes_shanjia/*
rm -rf /KingbaseES/kes_shanjia/.wallet
sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase restore

启动后查一下,你会发现 3 条数据全回来了:全量的 1 条、差异的 1 条、增量的 1 条。这个结果直观地说明了"备份链"的含义:一个增量备份能不能用,完全取决于它前面的全量以及中间所有的差异/增量是否完整。任何一环断了,整条链就废了。这也是为什么备份策略必须和 WAL 日志管理策略紧紧绑在一起。

七、永久增量备份:用合并平衡成本与效率

纯增量备份省时间省空间,但有个硬伤:恢复链会越来越长,链子一长,恢复效率就直线下降。金仓的解法是"永久增量备份",核心是 merge 操作,把多个历史备份集"压实"成一个新的全量。

手动合并是这样的:

sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase \
--set=20260626-132418F_20260626-140541I --merge_action=merge-and-delete merge

这里 --merge_action 有两个常用取值得分清:merge-and-delete 是合并后把旧的全量备份集移除,merge-no-delete 是合并但保留旧全量。

你也可以在做增量备份的时候顺手合并,一步到位:

sys_rman --config=/data/kesbackup/rman/sys_rman.conf --stanza=kingbase \
--type=incr --merge_action=merge-and-delete backup

合并之后,增量备份集就变成了一个新的全量备份集,旧的基础全量被移除了。这么一来,你既享受了增量备份的"短平快",又在恢复时拥有了全量备份的高可靠性。对于存储紧张、RTO 要求严格的业务来说,这几乎是最优解。

至于定时自动备份,改好 sys_backup.conf 后,用脚本初始化并启动,再用 crontab -l 验证任务生效就行:

sh /KingbaseES/V9/.../Server/bin/sys_backup.sh init
sh /KingbaseES/V9/.../Server/bin/sys_backup.sh start
crontab -l

八、高危场景:误用旧备份覆盖了生产库,怎么救

前面都是常规演练,现在说一个真正让人后背发凉的事故。有运维同学没停业务、没备份当前数据,直接把 7 天前的历史备份集恢复到了正在运行的生产库上,结果当天所有新增和修改的数据全被旧数据覆盖了。

物理恢复是覆盖式操作,它会直接替换 data 目录下的文件,常规的撤销手段统统无效。这种时候,抢救能不能成功,取决于你接下来几分钟的动作。

两条黄金原则,务必死守:

第一,禁止重启数据库。没重启之前,数据库内存里还缓存着最新的事务数据,磁盘上也残留着没被完全覆盖的数据块,这些是抢救的唯一依据。一旦重启,覆盖结果就被固化,最新数据彻底没了。

第二,禁止写入任何数据。立刻暂停所有业务写入和运维操作,避免新数据把磁盘上的原始残留块也覆盖掉。

抢救步骤大致是这样:

先冻结现场,关闭业务接口禁止写入,但保持数据库进程运行,不重启不关闭。然后快速打包当前的 data 目录,把现场环境留存下来,防止二次损坏:

su - kingbase
tar -zcvf /data/kb_data_error_bak.tar.gz /home/kingbase/KingbaseES-V9R2/data

接着趁数据还没固化,用逻辑导出工具把内存里缓存的最新业务数据抢出来:

ks_dump -U system -d 业务库名 -f /data/latest_data_backup.sql

之后再走精准恢复的正路:停库,重建一个纯净的空数据目录避免残留文件冲突,然后用 PITR 恢复到误操作前一分钟:

sys_rman --backup-path /data/kb_backup restore --time="2026-06-17 10:29:00"

最后,把第三步抢出来的增量数据导回恢复后的库,业务数据就完整还原了。

九、把误操作挡在门外:标准化流程

这种事故,事后抢救再漂亮也是被动的,真正的功夫在事前预防。我总结几条生产环境必须强制执行的规矩:

恢复前必须先备份当前生产数据,留个兜底,这是底线中的底线。所有恢复脚本和命令,先在测试环境跑通再上生产。物理恢复优先恢复到新目录,验证数据无误后再替换生产库,绝不直接覆盖。生产库的恢复操作,必须双人复核命令、备份集和时间点,确认无误才执行。

写在最后

走完 sys_rman 这一整套流程,我最大的感受是:可靠的数据保护从来不是靠某个强大的工具单打独斗,而是靠一套规范、可复制的操作流程。从环境准备到清理还原参数,每一环都不容有失。

物理备份恢复是高危操作,覆盖式恢复不可逆。但只要你建立起"备份链"和"恢复点"的概念,理解了 PITR 暂停机制的良苦用心,再加上严格的标准化流程,就能在 DROP TABLE 这样的致命误操作面前从容地把数据救回来。

我的建议永远是那句老话:所有操作先在非生产环境演练。一次成功的恢复,背后是无数次枯燥的备份演练。功夫在日常,这话用在数据库运维上,再贴切不过了。