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 级的改完必须重启,sighupsuperuser 级的可以 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;

UPDATEDELETE 都能这么救,只要 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 TABLEUPDATE 更吓人,表直接没了。但金仓开了回收站之后,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 TABLEPURGE 选项的话表不进回收站,直接删了,比如 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,那滋味真不好受。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐