KingbaseES 闪回实战:从误删恢复到历史版本追踪
KingbaseES 闪回实战:从误删恢复到历史版本追踪
前阵子群里有人问,说有人 UPDATE 忘了加 WHERE,全表数据被改了,有没有办法快速回滚。有人回答说从昨晚的备份恢复,可那一恢复就是十几分钟停机,业务根本等不了。其实金仓有个功能叫闪回,不用停库、不用动备份,一条 SQL 就能把数据查回误操作之前的状态,甚至直接把表闪回到那个时刻。这东西用了就知道有多香。
之前我写过 sys_rman 的物理备份和 PITR,那套是整机级别的恢复,要停库、铺备份、重放 WAL,分钟级起步。闪回不一样,它是逻辑层面的,基于 undo 数据,秒级就能查到历史数据或恢复误操作,业务完全无感。两者不是替代关系,是互补——小范围误操作用闪回,整机崩溃才动 PITR。
下面在一台 V9R1C10 上把金仓的三类闪回功能跑一遍:闪回查询、闪回表、回收站恢复。所有语法以官方手册为准。

先把闪回功能开起来
闪回靠 kdb_flashback 插件实现,得在 kingbase.conf 里加载,改完要重启:
# 加载闪回插件(重启级)
shared_preload_libraries = 'kdb_flashback'
# 记录事务提交时间戳,闪回查询的前提
track_commit_timestamp = on
# 开启回收站(reload 级,不用重启)
kdb_flashback.db_recyclebin = on
track_commit_timestamp 这行容易被忽略——不开的话 AS OF TIMESTAMP 根本用不了。我第一次配的时候就漏了这条,闪回查询一直报错,查了半天才意识到。kdb_flashback.db_recyclebin 控制 DROP 的表进不进回收站,sighup 级别,改完 sys_ctl reload 就行,不用重启。
重启完验证一下插件确实加载了:
SHOW shared_preload_libraries;
-- 应该能看到 kdb_flashback
SHOW kdb_flashback.db_recyclebin;
-- 应该是 on
还可以顺手查一下闪回相关的几个参数当前值,确认都符合预期:
SELECT name, setting, context, boot_val
FROM sys_settings
WHERE name LIKE 'kdb_flashback%' OR name = 'track_commit_timestamp'
ORDER BY name;
-- name | setting | context | boot_val
-- ---------------------+-----------+------------+----------
-- kdb_flashback.db_recyclebin | on | sighup | off
-- kdb_flashback.enable_flashback_query| on | superuser | on
-- kdb_flashback.enable_flashback_table| on | superuser | on
-- kdb_flashback.retain_seconds | 3600 | superuser | 3600
-- track_commit_timestamp | on | postmaster | off
context 列很重要——postmaster 级的改完必须重启,sighup 和 superuser 级的可以 reload 或在线改。这张表能一眼看清哪些参数能动态调、哪些要重启,比记在脑子里强。
闪回查询:查历史某一刻的数据
先建张测试表灌点数据:
CREATE TABLE emp (
emp_id NUMBER PRIMARY KEY,
emp_name VARCHAR(32),
salary NUMBER(10,2)
);
INSERT INTO emp VALUES (1, '张三', 8000);
INSERT INTO emp VALUES (2, '李四', 9500);
现在模拟一个事故场景。假设现在是下午 3 点,有人执行了 UPDATE emp SET salary = 0;——忘了带 WHERE,全表工资归零。完了,员工工资全没了。
先别慌。记下当前时间,然后查一下误操作之前的数据。用 AS OF TIMESTAMP:
-- 查 5 分钟前的数据(误操作之前)
SELECT * FROM emp AS OF TIMESTAMP now() - INTERVAL '5 minutes';
这一条就能把 5 分钟前那张表的数据查出来,工资还是 8000 和 9500。对比一下误操作前后:
-- 误操作之后(当前表里数据)
SELECT * FROM emp;
-- emp_id | emp_name | salary
-- --------+----------+--------
-- 1 | 张三 | 0
-- 2 | 李四 | 0
-- 误操作之前(5 分钟前的快照)
SELECT * FROM emp AS OF TIMESTAMP now() - INTERVAL '5 minutes';
-- emp_id | emp_name | salary
-- --------+----------+--------
-- 1 | 张三 | 8000
-- 2 | 李四 | 9500
对比一目了然,确认无误后,直接把数据导回去:
-- 用闪回查询的数据把被改坏的行恢复回来
UPDATE emp e SET salary = (
SELECT salary FROM emp AS OF TIMESTAMP now() - INTERVAL '5 minutes' h
WHERE h.emp_id = e.emp_id
);
就这么简单。全程不用停库,不用动备份,业务甚至感觉不到出了事。
如果是 DELETE 误删了几行,思路一样——从闪回快照里把被删的行查出来再插回去:
-- 假设有人误删了 emp_id=1 这行
DELETE FROM emp WHERE emp_id = 1;
-- 从 5 分钟前的快照查出被删的行,插回去
INSERT INTO emp
SELECT * FROM emp AS OF TIMESTAMP now() - INTERVAL '5 minutes'
WHERE emp_id = 1;
UPDATE 和 DELETE 都能这么救,只要 undo 还在保留期内。
除了按时间,也能按 CSN(事务序列号)查。先拿到当前 CSN:
SELECT pg_current_commit_seqno();
-- 假设返回 65536000023
然后:
SELECT * FROM emp AS OF CSN 65536000023;
CSN 比 TIMESTAMP 更精确,因为同一秒里可能有好几个事务提交,时间戳精度不够的时候 CSN 能精确定位到某个事务之后的状态。实际操作的时候可以先记下误操作前的 CSN:
-- 在做危险操作之前,先记下当前 CSN
SELECT pg_current_commit_seqno() AS csn_before_mistake;
-- 出事之后用这个 CSN 做闪回查询,精确无误
SELECT * FROM emp AS OF CSN :csn_before_mistake;
养成这个习惯——动手改数据之前先记一下 CSN,比记时间戳靠谱多了。
版本查询:看数据怎么变的
有时候你不光想知道"之前是什么样",还想知道"这一行被改过几次、每次改成什么"。金仓支持版本查询,用 VERSIONS BETWEEN:
SELECT versions_startcsn, versions_endcsn, versions_operation, emp_id, salary
FROM emp VERSIONS BETWEEN CSN MINVALUE AND MAXVALUE
WHERE emp_id = 1;
这个查出来的是张三这一行的变更历史,每行一个版本。versions_startcsn 是这个版本开始的 CSN,versions_endcsn 是结束的 CSN,versions_operation 是操作类型(I/U/D 分别代表插入、修改、删除)。看着像审计日志,但比审计日志细——它是行级的,精确到每一行的每一次改动。
为了看清楚效果,先做几次连续变更:
INSERT INTO emp VALUES (1, '张三', 8000); -- CSN 100:插入
UPDATE emp SET salary = 9000 WHERE emp_id = 1; -- CSN 101:改工资
UPDATE emp SET salary = 10000 WHERE emp_id = 1; -- CSN 102:又改
DELETE FROM emp WHERE emp_id = 1; -- CSN 103:删了
然后查这条行的版本历史:
SELECT versions_startcsn, versions_endcsn, versions_operation, emp_id, salary
FROM emp VERSIONS BETWEEN CSN MINVALUE AND MAXVALUE
WHERE emp_id = 1;
-- versions_startcsn | versions_endcsn | versions_operation | emp_id | salary
-- -------------------+-----------------+--------------------+--------+--------
-- 100 | 101 | I | 1 | 8000
-- 101 | 102 | U | 1 | 9000
-- 102 | 103 | U | 1 | 10000
-- 103 | | D | 1 |
每行一个版本,versions_endcsn 为空的那行是最后一次操作(删了之后没后续)。versions_operation 的 I/U/D 一目了然。看到这个结果,想恢复到哪一步就直接 AS OF CSN 查那个版本的数据。
排查"谁在什么时候改了这条数据"特别有用。有次业务方说某条订单金额不对,怀疑被人改过,我用版本查询一把就定位到了变更时刻,再对照应用日志就找到是谁操作的了。
闪回表:整表回到过去
如果误操作影响范围太大,一条条 UPDATE 恢复不现实,可以直接闪回整张表到某个时刻:
FLASHBACK TABLE emp TO TIMESTAMP '2025-08-05 14:55:00';
这一条把 emp 表的数据整体还原到 14:55 那一刻。注意它只回滚数据,表结构不变。闪回前后对比一下:
-- 闪回前(误操作之后的状态)
SELECT * FROM emp;
-- emp_id | emp_name | salary
-- --------+----------+--------
-- 1 | 张三 | 0
-- 2 | 李四 | 0
-- 执行闪回
FLASHBACK TABLE emp TO TIMESTAMP '2025-08-05 14:55:00';
-- 闪回后(数据已恢复到 14:55 那一刻)
SELECT * FROM emp;
-- emp_id | emp_name | salary
-- --------+----------+--------
-- 1 | 张三 | 8000
-- 2 | 李四 | 9500
也能按 CSN 闪回,比时间戳更精确:
FLASHBACK TABLE emp TO CSN 65536000023;
闪回过程中默认禁用触发器,如果你有触发器想让它跟着跑,加 ENABLE TRIGGERS:
FLASHBACK TABLE emp TO TIMESTAMP '2025-08-05 14:55:00' ENABLE TRIGGERS;
闪回表有个前提——undo 数据得还在保留期内。保留时间由 kdb_flashback.retain_seconds 控制,默认通常够用,但如果误操作是三天前的事,undo 早被覆盖了,闪回就查不到了。调大保留时间:
-- 在线调整保留时间(superuser 级,reload 即可)
ALTER SYSTEM SET kdb_flashback.retain_seconds = '7200'; -- 2 小时
SELECT sys_reload_conf();
-- 确认生效
SHOW kdb_flashback.retain_seconds;
实在来不及就只能上 PITR。所以闪回能解决的是"刚发生不久"的误操作,不是万能的。
回收站:DROP 了也能捞回来
DROP TABLE 比 UPDATE 更吓人,表直接没了。但金仓开了回收站之后,DROP 的表不会真删,而是被重命名后放进回收站。查一下:
SHOW RECYCLEBIN;
-- 或者
SELECT * FROM recyclebin;
能看到被删的表,对象名变成 BIN$xxxxx 这种格式。查一下回收站详情:
-- 查回收站里有哪些对象、原名叫什么
SELECT oid, original_name, type, droptime
FROM recyclebin;
-- oid | original_name | type | droptime
-- -----------+---------------+-------+---------------------
-- 16387 | EMP | TABLE | 2025-08-05 15:30:00
-- 16388 | PK_EMP | INDEX | 2025-08-05 15:30:00
original_name 就是原来的表名,droptime 是删的时间,靠这两列就能认出来。恢复很简单:
-- 直接闪回(当前没有同名表时)
FLASHBACK TABLE emp TO BEFORE DROP;
-- 当前已有同名表时,必须改名
FLASHBACK TABLE emp TO BEFORE DROP RENAME TO emp_restored;
闪回恢复时关联的索引、约束也会一起回来,不用单独处理。恢复之后回收站里这条记录就清掉了:
-- 确认表回来了
SELECT * FROM emp;
-- 确认回收站已清空对应记录
SELECT count(*) FROM recyclebin WHERE original_name = 'EMP';
-- 0
如果确认不需要恢复,也可以彻底清掉省空间:
-- 清掉单个对象
PURGE TABLE "BIN$abc123";
-- 清空整个回收站
PURGE RECYCLEBIN;
-- 清完确认
SELECT count(*) FROM recyclebin;
-- 0
有几点要注意。第一,DROP TABLE 加 PURGE 选项的话表不进回收站,直接删了,比如 DROP TABLE emp PURGE;,这个要慎用。第二,单独删的子表、临时表、分区不支持闪回,只有整表 DROP 才进回收站。第三,回收站会占空间,别一直不清理,定期 PURGE RECYCLEBIN 一下。
几个实战小坑
闪回查询报错说功能没开。 大概率是 track_commit_timestamp 没开。这个参数是 postmaster 级的,改完必须重启。好多人改了 shared_preload_libraries 重启了,但这条忘了加,闪回查询就一直用不了。排查的时候先确认一下:
SHOW track_commit_timestamp;
-- 如果是 off,改 kingbase.conf 加上 track_commit_timestamp = on 再重启
闪回表报 undo 数据不够。 误操作时间太久,undo 被覆盖了。kdb_flashback.retain_seconds 调大一点能延长保留时间,但占空间也多,得权衡。实在来不及就只能上 sys_rman 的 PITR 了。
AS OF TIMESTAMP 查出来数据对不上。 时间精度问题。同一秒内多个事务的话,用时间戳定位可能不准。换 CSN 最精确,pg_current_commit_seqno() 拿到当前值再查。
-- 时间戳精度不够时换 CSN
SELECT pg_current_commit_seqno() AS now_csn;
-- 用查出来的 CSN 做闪回查询
SELECT * FROM emp AS OF CSN :now_csn;
回收站里表名变了找不到。 DROP 之后表名变成 BIN$xxxx,原来的名字查不到。用 SELECT * FROM recyclebin; 看 original_name 列就能对上。
闪回表后触发器没跑。 默认就是禁用的,这是设计如此,防止恢复数据时触发器捣乱。需要触发器跑就加 ENABLE TRIGGERS。
-- 恢复数据时让触发器跟着跑
FLASHBACK TABLE emp TO TIMESTAMP '2025-08-05 14:55:00' ENABLE TRIGGERS;
最后
闪回这东西,平时用不上,用上的时候就是救命的。它和 sys_rman 的 PITR 是两个层级的恢复手段——闪回是逻辑级、秒级、在线,PITR 是物理级、分钟级、要停库。小范围误操作优先用闪回,快且不影响业务;整机崩溃或时间太久 undo 没了,再上 PITR。
建议生产上把 kdb_flashback 插件配上,track_commit_timestamp 和回收站都开着,平时不占多少资源,真出事的时候能省你大半天功夫。我就是吃过亏才这么说的——有次没有闪回,一个误 UPDATE 让我翻了一晚上 binlog,那滋味真不好受。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)