0
2
2
0
博客/.../

TiDB 7.5 BR 日志备份保留与清理实践

 认证小秘书  发表于  2026-08-22

前言

前面两篇分别做了 BR 快照恢复和 PITR。

快照恢复那篇比较简单,备份在哪个时间点,恢复后就回到哪个时间点。后面又加上日志备份,继续测试了 PITR,把集群恢复到了误操作之前。

做到这里以后,我本来想继续看看别的功能,结果 MinIO 里的备份目录先提醒了我一个问题:

日志一直在写,空间也一直在涨。

日志备份和普通的全量备份不太一样。全量备份一般是一份一份的,哪天做的、占了多少空间,看目录就能大概判断。日志备份是连续写的,只要业务还在跑,文件就会不断增加。

测试环境数据量不大,短时间内当然没什么压力。但如果放到长期运行的环境里,不做清理肯定不现实。

问题是,这些日志又不能看着日期旧了就直接删。

PITR 恢复时,需要先找到目标时间之前的一份全量备份,然后再把这份全量备份之后的日志继续应用到目标时间。如果中间哪一段日志被删掉了,前后的文件都还在也没有意义。

所以这次没再折腾恢复,而是把注意力放到一个更实际的问题上:

日志备份跑久了以后,到底该怎么清理,既能省空间,又不把后面的恢复搞坏。

一、先看看现在有多少备份

测试环境还是前面那套,没有换。

项目 配置
TiDB / BR v7.5.7
源集群 PD 192.168.56.101:2379
BR 中控机 192.168.56.110
MinIO 192.168.56.120:9000
Bucket tidb-br
日志备份任务 pitr_lab
日志目录 pitr-lab/log-backup
时区 Asia/Shanghai

日志备份是从 8 月 6 日开始的,后面每两天做一次全量快照。

到 8 月 14 日时,MinIO 里大概是这个样子:

s3://tidb-br/pitr-lab/
├── log-backup/
├── snapshot-20260806-093000/
├── snapshot-20260808-093000/
├── snapshot-20260810-093000/
├── snapshot-20260812-093000/
└── snapshot-20260814-093000/

日志目录一直没停,全量备份也在不断往后增加。

BR 中控机上还是使用之前的环境变量:

export LOG_STORAGE='s3://tidb-br/pitr-lab/log-backup?endpoint=http://192.168.56.120:9000&force-path-style=true'

先看一下日志任务:

tiup br:v7.5.7 log status \
  --task-name pitr_lab \
  --pd "192.168.56.101:2379"

输出里有两个东西我现在会习惯性多看一眼:

status: ● NORMAL
checkpoint[global]: 2026-08-14 18:08:42.317 +0800; gap=3m21s

NORMAL 说明任务还在正常跑。

但真正让我更关心的是下面这个 checkpoint[global]

它往前走,才说明日志确实还在持续写到备份存储里。要是哪天任务显示 NORMAL,但是 checkpoint 半个小时都没动,那就不能只看着绿色状态放心了。

这一点和传统备份任务有点像。很多时候“任务在运行”和“数据已经安全落盘”不是完全一回事。

二、我先给测试环境定了 5 天保留时间

这是个人测试环境,没必要留一个月。

我先按 5 天算。

当前时间是:

2026-08-14 18:20:00 +0800

按 5 天往前推,理论上只需要保留:

2026-08-09 18:20:00 +0800

之后的恢复能力。

一开始我看到这个时间,第一反应也挺直接:

那就把 8 月 9 日以前的日志和全量备份都删掉。

但真准备动手的时候,发现不能这么算。

比如现在要恢复到:

2026-08-09 20:00:00 +0800

这个时间之前最近的一份全量备份是哪一份?

不是 8 月 10 日,因为 8 月 10 日已经晚了。

只能用:

snapshot-20260808-093000

也就是说,虽然这份 8 月 8 日的快照已经超过 5 天,但它现在还不能删。

因为想恢复到 8 月 9 日,就必须先从 8 月 8 日这份全量备份开始,再接着应用后面的日志。

这个地方如果只是按“保留 5 天”去机械删目录,很容易把最前面的恢复能力一起删掉。

所以实际判断时,不能只看“5 天以前有什么”。

应该反过来想:

我最早想恢复到哪一天?那个时间点之前最近的一份全量备份是哪一份?

找到这份全量备份以后,它就是当前必须留下来的起点。

三、先把要保留的全量快照找出来

当前最早需要恢复的时间是:

2026-08-09 18:20:00 +0800

往前找最近的一份全量快照:

snapshot-20260808-093000

这份不能删。

先定义一下地址:

export SNAPSHOT_KEEP='s3://tidb-br/pitr-lab/snapshot-20260808-093000?endpoint=http://192.168.56.120:9000&force-path-style=true'

接下来不是直接用目录名字里的 20260808-093000 当清理时间,而是读取 BR 这份备份真正的 BackupTS:

FULL_BACKUP_TS=$(
  tiup br:v7.5.7 validate decode \
    --field="end-version" \
    --storage "${SNAPSHOT_KEEP}" \
  | tail -n 1
)

echo "${FULL_BACKUP_TS}"

我觉得这里还是值得注意一下。

目录名是自己起的,主要方便人看。真正决定备份时间点的是 BR 元信息里的 TS。

所以后面删日志,我也直接用这个 BackupTS,不自己手工拼一个时间。

这样出错概率小一点。

四、第一次清日志,我还是先跑 dry-run

真正执行删除之前,我先跑:

tiup br:v7.5.7 log truncate \
  --until="${FULL_BACKUP_TS}" \
  --storage="${LOG_STORAGE}" \
  --dry-run

--dry-run 不会真的删除日志,只是先计算一下哪些内容会被清掉。

测试环境里其实没多少数据,就算删错了,大不了重新来。

但我还是觉得这个习惯有必要保留。

生产环境里日志备份可能已经几十 TB,真要是哪次 until 时间写错了,再想回来就麻烦了。

我主要确认两件事。

一个是 until 对应的是 8 月 8 日这份要保留的快照,不是 8 月 9 日那个“5 天保留”的时间。

另一个是清理范围没有越过这份快照。

没问题以后,再正式执行。

五、正式截断旧日志

命令也很简单:

tiup br:v7.5.7 log truncate \
  --until="${FULL_BACKUP_TS}" \
  --storage="${LOG_STORAGE}" \
  --yes

--yes 只是跳过交互确认。

这个操作不需要连接 TiDB 源集群,BR 直接操作备份存储里的日志数据。

清理完以后,我第一件事不是去看 MinIO 少了多少 GB,而是继续检查日志元数据:

tiup br:v7.5.7 log metadata \
  --storage="${LOG_STORAGE}"

清理前,日志最早是从 8 月 6 日开始的:

log-min-date="2026-08-06 09:20:14.100 +0800"
log-max-date="2026-08-14 18:08:42.317 +0800"

清理以后,最早时间已经往后推到了 8 月 8 日这份快照附近:

log-min-date="2026-08-08 09:30:xx.xxx +0800"
log-max-date="2026-08-14 18:08:42.317 +0800"

这里我其实不太在意最后几位时间是多少。

只要确认两个点就行:

旧日志确实删掉了,新的日志还在。

后面的日志任务也没有受到影响。

六、为什么不能直接进 MinIO 把旧日志目录删掉

日志文件多起来以后,直接进 MinIO 按日期删,看起来是最省事的。

比如:

log-backup/20260806
log-backup/20260807

看到旧目录就删,好像也没什么问题。

但 BR 的日志目录不是普通的归档目录。

除了实际数据文件,还有元数据和 checkpoint 相关信息。BR 恢复时是按这些元数据去找对应日志的。

如果自己直接把某些文件删掉,目录表面上可能还在,真正恢复时才发现文件缺失,那就比较难受了。

所以我这里坚持一个原则:

日志备份目录内部不手工删。

只用:

br log truncate

处理。

全量备份目录不一样。

比如:

snapshot-20260806-093000/
snapshot-20260808-093000/

这些本身就是一份一份独立的全量备份。

确认某份已经不属于当前恢复范围以后,可以直接把整个快照目录删除。

这样处理起来比较清楚,也不容易把 BR 内部日志结构破坏掉。

七、旧日志清掉以后,再删旧全量备份

现在当前恢复窗口最早依赖的是:

snapshot-20260808-093000

所以这份必须留。

再往前的:

snapshot-20260806-093000

已经没有继续保留的必要。

先看 MinIO:

aws \
  --endpoint-url http://192.168.56.120:9000 \
  s3 ls s3://tidb-br/pitr-lab/

当前是:

PRE snapshot-20260806-093000/
PRE snapshot-20260808-093000/
PRE snapshot-20260810-093000/
PRE snapshot-20260812-093000/
PRE snapshot-20260814-093000/

删除 8 月 6 日这份:

aws \
  --endpoint-url http://192.168.56.120:9000 \
  s3 rm \
  s3://tidb-br/pitr-lab/snapshot-20260806-093000/ \
  --recursive

最后保留下来的内容:

s3://tidb-br/pitr-lab/
├── log-backup/
├── snapshot-20260808-093000/
├── snapshot-20260810-093000/
├── snapshot-20260812-093000/
└── snapshot-20260814-093000/

现在再看这套备份,就比较顺眼了。

如果要恢复到 8 月 9 日,可以从 8 月 8 日快照开始。

如果恢复到 8 月 11 日,可以从 8 月 10 日快照开始。

后面的时间也是一样。

只要全量快照和它后面的日志是连续的,就还能正常恢复。

八、删完以后,我还是重新检查了一遍

备份清理最怕的不是删不掉,而是删得太干净。

空间确实回来了,等真正恢复的时候才发现中间断了一截,这种情况就比较尴尬。

所以清理以后,我又重新检查了一遍。

先看日志元数据:

tiup br:v7.5.7 log metadata \
  --storage="${LOG_STORAGE}"

确认最早日志时间还在 8 月 8 日快照附近,最新日志也还在继续往前。

再读取保留的快照:

tiup br:v7.5.7 validate decode \
  --field="end-version" \
  --storage "${SNAPSHOT_KEEP}"

确保这份全量备份本身还能正常读取。

最后看日志任务:

tiup br:v7.5.7 log status \
  --task-name pitr_lab \
  --pd "192.168.56.101:2379"

任务还是:

status: ● NORMAL

checkpoint 也在继续变化。

做到这里,我才算放心。

严格来说,最完整的检查当然还是再找一套空集群做一次 PITR。

不过前面已经专门做过一篇 PITR 恢复,这次主要是验证清理过程,所以没有再把恢复步骤完整重复一遍。

如果以后在生产环境调整备份保留策略,我还是会建议实际恢复一次。

这种事情只看“备份状态正常”意义不大。

九、日志备份长期跑,还有几个地方不能忘

1. NORMAL 不是万能的

最开始我也容易只盯着:

status: ● NORMAL

看。

跑久了以后会发现,checkpoint 更重要。

如果:

checkpoint[global]

一直停在那里不动,虽然任务还显示正常,但最新数据其实没有及时进入备份存储。

对生产环境来说,这直接影响 RPO。

TiDB 也提供了:

tidb_log_backup_last_checkpoint

这类指标,用来观察最新 checkpoint。

Grafana 里的 Backup Log 面板也可以直接看。

2. 临时停任务,别上来就 stop

如果只是临时暂停日志备份,比如维护存储或者排查网络问题,应该用:

br log pause

恢复时:

br log resume

不要动不动:

br log stop

因为 stop 会把任务本身停掉并清掉相关任务信息。

这个和 pause 不是一个概念。

另外 pause 也不能无限暂停。

暂停时间太长以后,历史 MVCC 数据被 GC 掉,再 resume 时就可能出现缺日志的问题。

测试环境里感觉不到,生产上就要特别注意。

3. 日志一直增长是正常的

日志备份不是异常日志,不是越少越好。

只要数据库还在有写入,备份目录增长就是正常现象。

真正需要管理的是:

  • 存储空间还能撑多久;
  • checkpoint 有没有持续推进;
  • 现有全量快照和日志是不是还能组成完整恢复范围;
  • 老数据什么时候可以安全清掉。

这里最容易犯的错误,就是单独盯着存储空间。

看到 MinIO 快满了,就急着删文件。

实际上先搞清楚恢复时间范围更重要。

十、以后我准备固定这么清理

这次做完以后,我给自己整理了一个比较简单的处理顺序。

先确定最早需要恢复到什么时候。

比如:

保留最近 5 天

然后不要直接删 5 天以前的数据。

先找 5 天保留窗口之前,最近的一份全量快照。

把这份当成锚点。

读取它的 BackupTS:

FULL_BACKUP_TS=$(
  tiup br:v7.5.7 validate decode \
    --field="end-version" \
    --storage "${SNAPSHOT_KEEP}" \
  | tail -n 1
)

先 dry-run:

tiup br:v7.5.7 log truncate \
  --until="${FULL_BACKUP_TS}" \
  --storage="${LOG_STORAGE}" \
  --dry-run

确认没问题:

tiup br:v7.5.7 log truncate \
  --until="${FULL_BACKUP_TS}" \
  --storage="${LOG_STORAGE}" \
  --yes

日志截断完成以后,再删除这份锚点之前的旧全量备份。

最后重新看:

br log metadata

以及:

br log status

确认最早恢复点、最新 checkpoint 和任务状态都正常。

结语

这次本来只是想给 MinIO 腾点空间,最后反而把 BR 的保留逻辑又重新梳理了一遍。

我一开始也差点按照“保留 5 天”直接去删 5 天以前的数据。

实际往恢复方向倒推以后才发现,不行。

如果想保留 8 月 9 日开始的恢复能力,那 8 月 8 日最近的这份全量备份就必须留下。

日志也只能删到这份全量备份对应的 BackupTS。

这个地方如果没想清楚,文件删得很干净,恢复能力也一起删掉了。

做到现在,这套 TiDB 测试环境里的 BR 我已经连续做了三块内容:

快照备份和单表恢复,日志备份和 PITR,再到这次日志清理和备份保留。

0
2
2
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论