引言
冷备(停库拷贝数据目录)确实很快,能把你直接“拉回到某个备份点”。但它也有个天生的短板:
你只能恢复到“备份那一刻”,没法恢复到“误操作前一分钟”。
偏偏生产事故里最常见的诉求就是这种“卡在时间点上”的需求:
- 12:30 误删了订单表
- 我想恢复到 12:29:59
这就是 **PITR(Point-In-Time Recovery,时间点恢复)**要解决的事情:
基线备份(base backup)+ 归档 WAL(相当于日记账)= 把恢复点精确到某个时间。
本文还是按 Windows + ksql 的路线来走,给你一套能照着做的 PITR 思路和步骤。考虑到不同 KingbaseES 版本在“恢复配置文件形态”上可能不一样(recovery.conf vs recovery.signal),下面会把两种写法都给出来,你按自己的版本选一条跑通就行。
@[toc]
一、PITR 必须具备的 3 个条件
PITR 不是“把某个开关打开就搞定”。要让它真的能用,至少得同时具备三件事:
- 基线备份(base backup):一份可用的物理备份(常见是冷备目录或物理备份集)
- 归档 WAL(archive):把 WAL 日志段持续归档到一个安全目录
- 可用的恢复命令(restore_command):恢复时能从归档目录把需要的 WAL 拿回来回放
如果你想用一句话把这事讲明白,可以这么写在文章里:
基线备份负责“起点”,归档 WAL 负责“把时间往前推”,时间点目标负责“停在哪里”。
二、开启归档:让 WAL 有地方可去(Windows)
2.1 创建归档目录
建议使用系列统一目录:
New-Item -ItemType Directory -Force -Path D:\KB_LAB\archive | Out-Null
2.2 找到配置文件 kingbase.conf
找配置文件我一般推荐两种方式:
1)用 ksql 查询数据目录:
SHOW data_directory;
2)在数据目录里找到 kingbase.conf(一般就在 data_directory 根目录或其子目录中)。
2.3 修改归档相关参数(示例)
归档能力的核心思路很简单:让数据库把 WAL 段文件“持续复制”到一个安全目录里。实现上通常会用到下面这些参数(具体参数名和值以你机器的版本为准,建议先 SHOW 或直接打开配置文件看一眼):
wal_levelarchive_modearchive_commandarchive_timeout(可选)
在 kingbase.conf 中找到/新增类似配置(示例,按你环境路径替换):
wal_level = 'replica'
archive_mode = on
archive_timeout = 180
archive_command = 'cmd /c copy /Y "%p" "D:\\KB_LAB\\archive\\%f"'
这几行配置的意思可以这么理解:
archive_mode = on:开启归档archive_command:当 WAL 段切换时,数据库会执行该命令把 WAL 拷贝到归档目录%p:WAL 段在本机上的完整路径%f:WAL 段文件名
archive_timeout:即便 WAL 不活跃也定期触发切换,避免一直不归档
2.4 重启实例使配置生效
用“服务管理器 services.msc”重启实例服务即可。
2.5 验证归档是否已开启(ksql)
SHOW archive_mode;
SHOW archive_command;
SHOW archive_timeout;
SHOW wal_level;
2.6 核心验证:归档目录是否真的在增长(必须做)
只看 SHOW archive_mode 还远远不够。最关键的一点是:归档目录里确实在产生 WAL 文件。不增长,等于没归档。
1)先在归档目录里观察文件数量(初始快照):
(Get-ChildItem D:\KB_LAB\archive -File | Measure-Object).Count
2)在库里制造一点 WAL(例如插入/更新几行数据):
INSERT INTO backup_demo.t_account(id, name, balance) VALUES (999, 'WAL测试', 1.00);
UPDATE backup_demo.t_account SET balance = balance + 1 WHERE id = 999;
3)等待一小段时间(archive_timeout 配了 180 秒就最多等 3 分钟),然后再看归档目录有没有增长:
(Get-ChildItem D:\KB_LAB\archive -File | Measure-Object).Count
2.7 可选加速:触发一次 WAL 段切换(以本机函数为准)
“先自查函数再执行”的写法,别把某个函数名写死:
1)先列出可能的切换函数(不同版本命名可能不同):
\df sys_switch*
2)如果存在类似 sys_switch_xlog() 或 sys_switch_wal() 的函数,再按函数签名执行一次。
这一节的关键点是:不承诺某个函数一定存在,而是教你用
\df自己找,找到再执行,这样步骤才更稳、更可落地。
三、准备基线备份(base backup)
PITR 的基线备份,本质上就是一份“可用的物理备份”。在 Windows 单机环境里,最省事的做法就是直接复用上一篇的冷备流程:
- 停库
- 拷贝数据目录到
D:\KB_LAB\backup\physical\... - 再启动库
建议把这份基线备份命名为:
D:\KB_LAB\backup\physical\20260207_1300_base_backup
同时一定要把“基线备份时间点”写清楚(这一步非常关键,后面恢复目标全靠它做参照):
- BaseBackupTime =
2026-02-07 13:00:00
3.1 关键提醒:基线备份与 WAL 归档必须“覆盖同一时间轴”
PITR 的可恢复范围取决于两个东西:
-
基线备份的起点(BaseBackupTime)
-
归档目录里从起点开始的 WAL 段是否连续、是否保存足够久
所以在运维预案里,建议把下面这条规则写死:
归档WAL的留存时限要包含从“上次基线备份”一直到“自己期望回退到的最迟时点”这一整段的WAL。
四、制造事故:模拟误操作并记录时间点
4.1 在备份之后制造新业务数据(让 PITR 的价值更明显)
比如新增一笔订单(这样你后面一眼就能看出 PITR 的价值):
INSERT INTO backup_demo.t_order(order_id, account_id, amount)
VALUES (2001, 1, 66.00);
顺手把当前时间记下来(后面验收要用到这个对照):
SELECT CURRENT_TIMESTAMP;
4.2 模拟事故:误删表
假设事故发生在 2026-02-17 13:20:00:
DROP TABLE backup_demo.t_order;
验证表没了:
\dt backup_demo.*
你的目标就是把数据库恢复到:
- RecoveryTargetTime = 2026-02-17 13:19:59
五、执行 PITR 恢复(核心步骤)
PITR 恢复的“通用套路”其实就一套固定动作:
- 停库
- 用基线备份覆盖数据目录
- 配置恢复参数(告诉数据库:从哪取 WAL、恢复到哪)
- 启动库进入恢复模式
- 恢复完成后“提升(promote)”并做验收
5.1 停库并还原基线备份目录
这一步和上一篇的物理恢复一致:
- 停止实例服务
- 当前数据目录改名留存
- 把
20260207_1300_base_backup拷贝回数据目录路径
5.2 配置 restore_command 与 recovery_target_time
不同内核版本的恢复配置方式可能不一样,你按自己的环境选其中一种跑通就行。
方式 A:recovery.signal(PostgreSQL 12+ 常见形态)
1)在数据目录根目录创建一个空文件 recovery.signal
2)在配置中写入恢复参数(两种写法任选一种):
写法 1:编辑 kingbase.conf 追加(或写入 kingbase.auto.conf):
restore_command = 'cmd /c copy /Y "D:\\KB_LAB\\archive\\%f" "%p"'
recovery_target_time = '2026-02-07 13:19:59'
recovery_target_action = 'promote'
写法 2:如果你更希望“恢复专用配置”和正常配置隔离,可以用 include 的方式单独维护(以你版本支持为准)。
方式 B:recovery.conf(较老版本常见形态)
如果你的环境仍然使用 recovery.conf,就在数据目录下创建 recovery.conf 并写入:
restore_command = 'cmd /c copy /Y "D:\\KB_LAB\\archive\\%f" "%p"'
recovery_target_time = '2026-02-07 13:19:59'
recovery_target_action = 'promote'
小提示:你可以直接在数据目录里搜一下有没有
recovery.conf或recovery.signal,通常就能判断你这套环境更偏向哪种形态。
5.2.1 把“时间点”写对:时区与格式建议
为了让 recovery_target_time 真正“写得进去、写得准”,建议你在制造事故前先把实例时区记下来:
SHOW TimeZone;
SELECT CURRENT_TIMESTAMP;
写 recovery_target_time 时建议注意两点:
- 按
YYYY-MM-DD HH:MM:SS格式 - 确保是实例时区下的时间(避免“你以为的 12:29:59”与实例日志里的时间不一致)
5.3 启动实例并观察恢复进度
启动实例服务后,数据库会进入恢复模式,大致会发生这些事:
-
持续从归档目录取 WAL
-
回放到目标时间点
达成目标并设置为自动提升(即recovery_target_action = 'promote'时),要判断其是否真的执行了提升操作,最可信的方法就是查看日志,可以从日志里找到相关信息来确认。
-
看数据库日志(恢复开始/恢复完成/提升成功都会写日志)
-
能否正常用 ksql 连接并查询对象
5.4 恢复后收尾:确认已退出恢复模式
1)再次连接 ksql,确认能正常执行写入(例如插入一行测试数据)
2)检查数据目录里是否还存在 recovery.signal(不同版本行为可能不同,但通常会在恢复完成后退出恢复状态)
六、恢复后验收:证明“回到了误操作前”
连接到 backup_lab:
ksql -U system -d backup_lab -h 127.0.0.1 -p 54321
验收建议盯住 3 件事:
1)被误删的表存在:
\dt backup_demo.*
2)数据回到目标时间点(比如 order_id=2001 是否存在,取决于你设定的目标时间):
SELECT * FROM backup_demo.t_order ORDER BY order_id;
3)其他表的数据一致:
SELECT COUNT(*) FROM backup_demo.t_account;
七、常见问题排查(PITR 高概率踩坑)
问题 1:归档目录没有生成 WAL 文件
检查:
archive_mode是否为 onarchive_command是否可执行(路径是否存在、权限是否足够)- 是否能通过
\df sys_switch*找到可用的 WAL 切换函数并触发一次(若函数可用)
问题 2:恢复时提示找不到 WAL 文件
原因通常是:
- 归档不连续(中间 WAL 缺段)
- 归档目录被清理过
- 目标时间点早于/晚于归档覆盖范围
解决:
- 确保归档目录覆盖从“基线备份点”到“目标时间点”之间的所有 WAL 段
问题 3:恢复后一直处于恢复模式或无法写入
原因可能是:
- 没有 promote(没有设置
recovery_target_action,或日志提示失败) recovery.signal/recovery.conf没有按版本要求处理
解决:
- 以日志为准定位
- 确认恢复配置是否与版本匹配
问题 4:restore_command 执行失败(路径/权限/引号问题)
Windows 上最容易出错的是三件事:
- 归档目录路径写错(建议全用绝对路径)
- 服务运行账户对归档目录没有读取权限
- 引号与反斜杠转义写错
排查时我建议先把 restore_command 简化成最朴素、最容易执行的命令,确认能跑通之后再一点点加复杂度(尤其是引号和路径)。
总结
PITR 的价值一句话就能说清:把“只能恢复到备份点”升级成“可以恢复到误操作前一分钟”。
这篇文章给了一条 Windows 上可落地的 PITR 路线:
-
开启归档(
archive_mode+archive_command) -
做基线备份(冷备即可)
恢复设置里包含restore_command和recovery_target_time两项配置。
启动进入恢复并验收此篇,我们要把这套体系自动化,利用Windows任务计划执行定时备份,保存日志并保留相关策略,还要使失败具备追溯性,从而将备份由“手工活”转为成为“制度化能力”。