行业资讯

【金仓数据库征文】误删表拯救实录:sys_rman 全备、增备与按时间点恢复实测

发布时间:2026/8/8 7:00:12
【金仓数据库征文】误删表拯救实录:sys_rman 全备、增备与按时间点恢复实测 为什么要亲手删一次表起因是我发现自己答不上来一个问题这台机器上的 KES 真出了事,我多久能把数据找回来配置里 archive_mode 是开着的,理论上全备加 WAL 归档就能做按时间点恢复PITR,Point-In-Time Recovery。可理论上能和我亲手做成过,中间隔着一次真刀真枪的演练。所以有了这篇文章在一台鲲鹏云服务器麒麟 V10 KingbaseES V9上建好备份防线,让业务正常写一阵,然后亲手 DROP 掉一张 30.7 万行的订单表,再把它按时间点捞回来。先把结果放这,免得你划到最后132MB 的库全备 8 秒、增备 1.5 秒表删掉之后按删除前一刻恢复,restore 本体 571 毫秒,30.7 万行一行不少,连增量备份之后才写进去、只存在于 WAL 归档里的 2000 行也回来了。过程里还踩了一个跟now()有关的坑,差点丢数据,后面细说。整个演习的时间线长这样图 1 说明从建立防线到验证恢复的全过程时间线,时刻与数字均来自图 4 至图 9 的实测输出。环境先交底。机器是华为云的 4 核 ECS,7.1G 内存,HiSilicon 鲲鹏aarch64,系统麒麟 V10,数据库 KingbaseES V009R001C010 跑在 54321 端口,数据目录 /data,盘上还剩 27G 可用。备份工具用 KES 自带的 sys_rman,就在 Server/bin 里,不用额外装图 2 说明麒麟 V10 aarch64,KingbaseES V009R001C010,sys_rman 同版本随库自带,系统盘可用 27G。简单介绍下这个工具。sys_rman 是物理备份管理器,管全量/增量备份、备份保留策略、WAL 归档,也管按时间点恢复。它的组织方式是仓库 stanza仓库是备份落盘的地方,stanza 是实例的配置单元,一个仓库可以同时管好几个实例。搭防线防线要干三件事数据库开归档、sys_rman 配好仓库、跑一次端到端体检。数据库这边改两个参数。archive_mode on要重启才生效,我在实例初始化完就顺手设了archive_command指到 sys_rman 的 archive-push,之后每个写满的 WAL 段会自动推进仓库archive_mode on archive_command /opt/Kingbase/ES/V9/KESRealPro/V009R001C010/Server/bin/sys_rman --stanzademo archive-push %psys_rman 这边的配置在/etc/sys_rman/sys_rman.conf,十来行。其中三行我要多嘴start-fasty,备份开始时立即做检查点,不等自然检查点周期。我一开始没设,全量备份跑了 158 秒,设上之后 8 秒,差的那 150 秒基本全在干等检查点。repo1-retention-full2,只留最近两次全备,更早的自动清,不设的话每次备份都弹一条仓库可能爆盘的 WARN。日志开了双通道,控制台 info、文件 detail,出问题好翻旧账。然后stanza-create初始化,check体检图 3 说明归档参数、完整配置文件、stanza-create28ms与 check 全过程check 主动触发了一次 WAL 归档并确认段文件落进 /kbbak/archive 目录,端到端链路验证通过。check 这步别省。它不是读读配置就完事,是真触发一次 WAL 切换、真等归档落盘,把数据库到仓库这条管道整个过了一遍水。链路有毛病,这一步就炸出来了,总好过恢复的时候才发现备份是残的。第一次全备库里 300000 行订单,开备图 4 说明--typefull全量备份,132.2MB、2811 个文件,8049ms 完成,备份标签 20260806-154957F结束后 retention 策略自动检查expire 6ms。8 秒出头,132MB 拿下。输出里我盯了两行一行backup begins after the requested immediate checkpoint completes,这是 start-fast 在干活另一行wait for all WAL segments to archive,备份收尾前会确保对应的 WAL 全部归档到位,备份集的自洽就靠它。业务不停,增备跟上真实系统不会为了备份停机。往 orders 里追加 5000 行已支付订单,总量到 305000,然后做增量图 5 说明插入 5000 行后执行--typeincr,sys_rman 自动找到上次全备做基底last backup label 20260806-154957F,逐文件比对后只备份变化的 10 个文件共 27.5MB,1513ms 完成,标签 20260806-154957F_20260806-155157I。增备的输出把机制摆得明明白白逐个列出实际拷贝的文件,大头是 orders 的两个数据文件20.4MB 加 6.6MB,剩下是些事务日志相关的小件。132MB 的库,增量只动 27.5MB,耗时从 8 秒缩到 1.5 秒。info看一眼全景图 6 说明一全一增两个备份集。全备在仓库占 21.8MB132.2MB 压缩后,增备占 7.5MB增备的 reference list 指向全备,恢复时会自动组合。出事了防线就位,现在制造事故。为了贴近真实,先让业务再写 2000 行待支付订单,总量 307000。注意这 2000 行的处境它们在任何备份集里都不存在,只活在 WAL 归档里。然后,手一抖图 7 说明追加 2000 行后,先用单独的一条select now()把当前时间记进 /tmp/rescue_point16:13:22.89781208,确认 307000 行在库,然后 DROP TABLE orders。再查,relation orders does not exist,30.7 万行没了。图里那条单独执行的select now(),是我用血泪换来的写法。预演的时候我图省事,把now()跟 insert 塞在同一条命令里,结果恢复出来只有 305000 行,最后 2000 行没了。查了半天才想通now()给的是事务开始时间,可按时间点恢复认的是事务的提交时间。同一个事务里,提交必然晚于now(),恢复目标点就落在了这笔提交之前,整批数据被排除。改成写入提交之后再单独取时间戳,坑就绕开了。多说一句,真出事故的时候没人会贴心地帮你记好删除前的时间点。实际上这个时间来自业务报障、数据库日志里 DROP 语句的时间戳,或者 sys_stat_statements 的记录。来源不同,用途一样给恢复找一个事故之前的落点。捞回来恢复三步停库、restore、起库。图 8 说明确认恢复点文件内容后停库sys_rman --stanzademo --delta --typetime --target$(cat /tmp/rescue_point | xargs) --target-actionpromote restore执行恢复,输出显示它自动选择增量备份集 20260806-154957F_20260806-155157I 作为基底recovery will start at 2026-08-06 15:51:57,571ms 完成 2811 个文件的处理随后一条 start,数据库回放 WAL 至目标点并提升对外服务。参数拆开讲。--delta是在现有数据目录上做差异恢复,只重写有出入的文件,571ms 处理完 132.6MB 靠的就是它。--typetime --target...指定按时间点恢复,目标就是刚才存在 /tmp/rescue_point 里的那个时间戳。--target-actionpromote让数据库回放到目标点后直接提升为可读写。restore 自己会改好 kingbase.auto.conf 里的恢复参数,起库之后的回放不用人管。有条 WARN 得如实交代。restore 一开始打了一行archived files within repo-1 have issues, check it as soon as possible,我翻了 detail 级日志,也没有更具体的上下文。本次恢复照常完成,数据全量验证通过下一节。恢复完我按提示跑了一次 check,新时间线上的归档链路正常INFO: WAL segment 000000020000000000000010 successfully archived to /kbbak/archive/demo/12-1/0000000200000000/... on repo1 INFO: check command end: completed successfully (126ms)碰到这类提示,恢复完跑一次 check、确认新时间线归档正常,处置上比较稳。对账恢复成功的标准不是表回来了,是数据经得起对图 9 说明count 返回 307000按状态分组,pending 精确为 77000原始 75000 删除前最后写入的 2000,paid 80000原始 75000 增备前写入的 5000timeline_id 变为 2最后执行恢复后的新基线全备,20260806-161631F,8110ms。总量 307000,说明恢复点选对了。pending 等于 77000,这是全文我最看重的一个数那 2000 行待支付订单不在任何备份集里,只在 WAL 归档里存着,它们完整回来了,说明备份打底、归档回放这条链是真通的,不是纸面功夫。timeline_id 从 1 变 2,数据库在目标点分叉出了新的历史线,这是一次正经的时间点恢复该有的样子。最后那次全备不是仪式感。恢复后数据库走上新时间线,老时间线上的增量链对新历史帮不上什么忙,第一时间重建全量基线,下一次出事才有干净的起点。这时候 retention 策略也开始转起来新基线进来,最老的全备自动出局。数字都在这本轮演习的关键数字,同一环境同一数据集,出处都在对应截图里操作耗时数据量仓库占用出处全量备份300000 行时8049ms132.2MB / 2811 文件21.8MB图 4、图 6增量备份305000 行时1513ms27.5MB / 10 个变化文件7.5MB图 5、图 6PITR restore 本体–delta571ms132.6MB / 2811 文件—图 8恢复后新基线全备307000 行时8110ms132.8MB / 2813 文件—图 9stanza-create / check / expire28ms / 秒级 / 6ms——图 3、图 4图 10 说明左图为四类操作耗时基线为 start-fasty 配置,不开该选项时全量备份实测 158 秒右图为备份数据量与仓库实际占用对比,gz 压缩后约为原始量的 1/6 至 1/4。数据来源图 4、5、8、9 的 sys_rman 输出。恢复完的数据对账单订单状态恢复后行数构成paid80000原始 75000 增备前写入 5000pending77000原始 75000 仅存在于 WAL 中的最后 2000cancelled / refunded各 75000原始数据合计307000与删除前记录值一致,零丢失出处图 7删除前、图 9恢复后。最后说几句回到开头的问题真出了事,多久能把数据找回来这台机器现在有实测答案了。从进救援会话到数据库带着全部数据重新对外服务,分钟级,restore 本体半秒多,大头其实是人敲命令的时间。策略上我也有了更具体的数。全备加增备这套组合,成本低到没理由不做8 秒和 1.5 秒的动作对多数业务无感,仓库空间压到原始量的两成以下,retention 自动控制总量。按时间点恢复的边界也摸清了能精确到微秒级时间戳,能救回只活在 WAL 归档里的数据代价是目标点之后的写入全部作废。发现得越晚,作废得越多,所以它适合的是误操作之后尽快发现的场景。坑也记下了两个。恢复点时间戳必须在目标事务提交后单独取,now()是事务开始时间,这个上面讲过了。restore 时那条归档 WARN 的确切含义,我到现在没从日志里挖到底,先按恢复后 check 验证处置着后续版本要是能把 WARN 的原因打印得更具体些,排查会省不少事。接下来我打算把这场演习脚本化,挂上定时任务做月度自动演练自动起临时实例、恢复最近的备份、跑数据校验、出报告。备份可不可靠,不该靠上次演练时是好的撑着,得是上个月的演练报告说它是好的。