金仓数据库逻辑备份实战:从全库导出到 Schema 替换的完整闭环

做运维这些年,我越来越相信一句话:备份不是为了平时好看,而是为了出事那一刻能救命。逻辑备份这东西,看着简单,真到了要恢复的时候,各种坑就一个接一个冒出来了。表名大小写、schema 不一致、owner 没跟着换,哪一个处理不好,恢复出来的库都不是你想要的那个样子。

这篇文章我想把金仓数据库(KingbaseES)逻辑层面的备份与恢复彻底讲透,从最基础的全库导出,一直聊到恢复时怎么把旧 schema 干净地换成新的。内容都是平时踩过坑、验证过的东西,希望能让你少走点弯路。

一、逻辑备份的三种导出格式

金仓的逻辑备份核心工具是 sys_dump,它支持三种导出格式,分别对应不同的场景。我习惯先把这三种格式的区别讲清楚,因为后面恢复用什么命令,完全取决于你导出时选了哪种。

第一种是纯文本格式,参数是 -Fp,导出的就是一个普通的 .sql 文件,里面全是可读的 SQL 语句。这种格式最大的好处是透明,你可以直接打开看,甚至能用文本编辑器改。

bash 复制代码
# su - kingbase
$ mkdir -p /home/kingbase/backup/sys_dump_$(date +%Y%m%d%H)
$ sys_dump -h 127.0.0.1 -p 54321 -U system -d dbtest -f /home/kingbase/backup/sys_dump_$(date +%Y%m%d%H)/dbtest.sql

第二种是自定义二进制格式,参数 -Fc,导出的是一个 .dmp 文件。这种格式压缩过,体积小,而且恢复时灵活度高,可以选择性地只恢复某些对象。

bash 复制代码
# su - kingbase
$ mkdir -p /home/kingbase/backup/sys_dump_$(date +%Y%m%d%H)
$ sys_dump -h 127.0.0.1 -p 54321 -U system -d dbtest -Fc -f /home/kingbase/backup/sys_dump_$(date +%Y%m%d%H)/dbtest.dmp

第三种是目录格式,参数 -Fd,它会把备份内容拆成一个目录里的多个文件。这是唯一支持并行备份的格式,通过 -j 参数指定并行度,大库备份能快不少。

bash 复制代码
# su - kingbase
$ sys_dump -h 127.0.0.1 -p 54321 -U system -d dbtest -Fd -j 4 -f /home/kingbase/backup/sys_dump_$(date +%Y%m%d%H)

要注意目录格式有个小细节:如果目标目录已经存在,得先删掉再备份,不然会出问题。

二、不只是全库,还能精细到 Schema 和单表

实际工作里,很多时候我们并不需要整库备份,可能只想保住某个 schema 或者某张关键表。sys_dump-n 指定 schema,用 -t 指定表。

只备份某个 schema:

bash 复制代码
$ sys_dump -h 127.0.0.1 -p 54321 -U system -d dbtest -n public -f /home/kingbase/backup/sys_dump_$(date +%Y%m%d%H)/public.sql

只备份某张表:

bash 复制代码
$ sys_dump -h 127.0.0.1 -p 54321 -U system -d dbtest -t test -f /home/kingbase/backup/sys_dump_$(date +%Y%m%d%H)/test.sql

这里有几个我反复提醒新人的点:加 -a 参数表示只备份数据不要结构,加 -s 参数则相反,只要结构不要数据。还有一个容易被忽略但很关键的特性:sys_dump 只转储备份开始那一刻的快照,也就是说备份过程中发生的数据变更不会被包含进去。这个机制保证了备份的一致性,但你心里得清楚这条边界在哪。

三、那个最容易让人栽跟头的坑:表名大小写

说到 -t 指定表名,我必须单独拎出来讲一个坑,因为它实在太典型了。当你的表名是大小写混合的时候,sys_dump 经常会报 no matching tables were found,明明表就在那儿,它就是找不到。

问题的根子不在数据库,而在 Linux 的 shell 语法。我们先理解一下金仓里表名是怎么存的。在大小写不敏感的环境下(enable_cion),你建表时不加双引号,表名会被统一转成小写存进数据字典。

sql 复制代码
TEST=# create table dD (id int );
CREATE TABLE
TEST=# select relname from pg_class where relname='dd';
 relname
---------
 dd
(1 row)

但如果你建表时给表名加了双引号,它就会原样保留大小写混合的形态:

sql 复制代码
create table "gD" (id int );
TEST=# select relname from pg_class where relname='gD';
 relname
---------
 gD
(1 row)

这时候问题就来了。你在 sys_dump 命令里写 -t "hGF",本意是想保留大小写,但双引号在 shell 里会被先一步解析掉,传给 sys_dump 的其实只剩下 hGF,而数据字典里存的是带引号语义的标识符,两边对不上,自然就报错。

bash 复制代码
[kingbase2@localhost V8]$ sys_dump -U system -d test -p 2920 -FC -t "hGF" -f /opt/Kingbase/ES/V8/dd1.dmp
sys_dump: error: no matching tables were found

正确的写法有两种,核心都是让双引号能真正传到 sys_dump 那一层。第一种用转义符:

bash 复制代码
sys_dump -U system -d test -p 2920 -FC -t "\"hGF\"" -f /opt/Kingbase/ES/V8/db1.dmp

第二种用单引号把双引号包起来,因为 shell 里单引号内的内容是纯字符串,不做任何解析:

bash 复制代码
sys_dump -U system -d test -p 2920 -FC -t '"hGF"' -f /opt/Kingbase/ES/V8/db2.dmp

记住这个区别:shell 里单引号是字面字符串,双引号有特殊含义会触发变量解析,所以涉及大小写混合表名时,要么转义要么用单引号包裹。

顺带提一句大小写敏感和不敏感环境的差异。敏感环境(enable_cioff)下,小写表、大写表、混合表可以同时存在,互不冲突。而不敏感环境下,无论你写 agAg 还是 AG,系统只认其中一种,后面再想建另一种形态的同名表,就会直接报 relation already exists。这个特性在迁移和恢复时尤其要留心。

四、恢复:导出格式决定了你用什么命令

讲完备份,接下来是恢复。这里有个铁律:用什么命令恢复,取决于你当初是什么格式导出的。

纯文本的 .sql 文件只能用 ksql 恢复,这是没有商量余地的。恢复前记得先把数据库建出来。

bash 复制代码
# su - kingbase
$ createdb -h 127.0.0.1 -p 54321 -U system dbtest
$ ksql -h 127.0.0.1 -p 54321 -U system -d dbtest -f /home/kingbase/backup/sys_dump_2023060615/dbtest.sql

自定义格式和目录格式则用 sys_restore:

bash 复制代码
# 自定义格式
$ sys_restore -h 127.0.0.1 -p 54321 -U system -d dbtest /home/kingbase/backup/sys_dump_2023060615/dbtest.dmp

# 目录格式,同样支持并行
$ sys_restore -h 127.0.0.1 -p 54321 -U system -d dbtest -Fd -j 4 /home/kingbase/backup/sys_dump_2023060615

sys_restore 时有个参数细节得提醒:-d-f 不能一起用,你只要用 -d 指定目标库就行,不需要再用 -f 单独去指文件位置。这一点我见过不少人搞混。

恢复 schema 或单表的逻辑类似,如果当初导的是 .sql,那就还是 ksql-f:

bash 复制代码
$ ksql -h 127.0.0.1 -p 54321 -U system -d dbtest -f /home/kingbase/backup/sys_dump_2023060615/public.sql

五、进阶玩法:恢复时把旧 Schema 换成新的

最后聊一个稍微高级、但实战中很实用的需求:恢复的时候,我不想用原来的 schema,想换成一个新的。比如源库里数据都在 abc 这个 schema 下,我希望导进目标库时落到 u2 下面。

sys_restore 提供了 -g-G 参数来做这件事。但这里有个非常容易被忽略的陷阱:光加 -g -G,表确实会落到新 schema,可是表的 owner 还是老的!如果你连 owner 也想一起换掉,必须再加一个 -O 参数。

我用一个完整的测试来说明。先准备环境,建一个 abc 用户、abc 库、abc schema,里面放张 t1 表,owner 和 schema 都是 abc。再建一个超级用户 u2 和对应的 u2 schema。

导出 abc 库:

bash 复制代码
sys_dump -Uabc -Fc -f abc.dmp abc -p 2920

加上 -O-g-G 三个参数恢复,目标 schema 指定为 u2:

bash 复制代码
sys_restore -Uu2 -dabc -Fc -p 2920 -gabc -Gu2 -O abc.dmp

恢复完查一下,t1 表的 schema 和 owner 都变成 u2 了,这正是我们想要的:

sql 复制代码
abc-# \c abc u2
abc-# \d
               List of relations
 Schema |        Name         | Type  | Owner
--------+---------------------+-------+--------
 public | sys_stat_statements | view  | system
 u2     | t1                  | table | u2
(2 rows)

作为对比,如果不加 -O,你会看到 schema 虽然换成了 u2,但 owner 倔强地还是 abc:

sql 复制代码
sys_restore -Uu2 -dabc -Fc -p 2920 -gabc -Gu2 abc.dmp
-- 结果里 t1 的 Owner 仍然是 abc

除了用参数,还有一条更朴素的路子:导出成 .sql 文本,然后直接用 sed 命令把里面的 schema 名字全替换掉。

bash 复制代码
sys_dump -Usystem -Fp -f abc.sql abc -p 2920
sed -i 's/abc/u2/g' abc.sql
sed -i 's/public/u2/g' abc.sql
ksql abc -Usystem -f abc.sql

这种方式直观好懂,但有个明显短板:面对大数据量的库,文本替换会很慢,而且全局替换容易误伤(比如表名里恰好也含 abc 字样)。所以小库可以这么玩,大库我还是建议老老实实用 sys_restore 的参数。

写在最后

逻辑备份这套东西,真正的难点从来不是命令本身,而是那些藏在细节里的边界条件:三种格式各自的恢复方式、大小写混合表名在 shell 里的转义、sys_restore 的参数互斥、还有 owner 跟 schema 替换不同步的问题。把这些坑一个个填平,你的备份恢复流程才算真正可靠。

我的建议始终如一:生产环境一定要定期做恢复演练。备份文件躺在硬盘上不叫备份,能在需要的时候完整、正确地恢复出来,那才叫真正的容灾能力。

相关推荐
SelectDB1 天前
阶跃星辰基于 SelectDB 构建 PB 级 Agent 可观测平台
大数据·数据库·aigc
这个DBA有点耶1 天前
GROUP BY优化全解:如何写出既不丢数据又飞快的分组查询
数据库·mysql·架构
掉头发的王富贵1 天前
【StarRocks】极限十分钟入门StarRocks
数据库·sql·mysql
Nturmoils1 天前
WHERE 条件别凭习惯写,常用查询先跑一遍
数据库
Databend2 天前
在 AWS 中国峰会逛了一天,我在 Databend 展台看到了 Agent 数据基础设施的新思路
数据库·人工智能·agent
ClouGence3 天前
Oracle 数据同步为什么会出现数据不一致?长事务是常被忽略的原因
数据库·后端·oracle
飞将3 天前
从零实现数据库(2)——HashIndex + IndexManager
数据库
Nturmoils4 天前
订单列表慢查询,先看 WHERE、ORDER BY 和 LIMIT
数据库