首页
/ TiDB BR 向后兼容测试体系解析:跨版本备份恢复的完整验证工作流

TiDB BR 向后兼容测试体系解析:跨版本备份恢复的完整验证工作流

2026-09-05 09:24:20作者:晏闻田Solitary

本文基于 br/COMPATIBILITY_TEST.md 展开,介绍 TiDB BR(备份恢复工具)如何验证“用当前版本 BR 恢复历史版本备份数据”这一向后兼容能力:包括历史备份数据的准备方式、基于 docker-compose 的测试集群搭建,以及恢复验证脚本的执行细节。读完后,你可以完整理解 BR 兼容测试的三段式工作流(版本选择 → 数据准备 → 恢复校验),并能按仓库提供的脚本与 Makefile 目标独立复现整个流程。

背景与目标:为什么需要兼容性测试

TiDB 的备份归档(backup archive)本质上是对物理 KV 数据和元数据的持久化封装。随着版本迭代,归档格式、元信息结构都可能演进,而历史备份数据一旦写坏无法重做,因此必须回答一个关键问题:当前版本的 BR 能否正确恢复旧版本 BR 产生的备份?

仓库文档明确说明了引入该测试的动机与目标:

  • 背景:过去曾出现过不兼容问题,导致某些情况下 BR 无法恢复已备份的数据,因此需要一条专门的测试工作流来持续检查兼容性;
  • 目标:确保能够向后兼容恢复最近 3 个 minor 版本(previous 3 minor versions)的备份数据。

该测试策略覆盖两类对象存储——S3 与 GCS。由于要测试最近 3 个版本、且有 2 种存储系统,共产出 6 份备份归档,对应 6 个独立的兼容性测试用例(3 版本 × 2 存储)。

工作流总览

兼容测试分为两个阶段:

  1. 数据准备(Data Preparation):分别拉起旧版本的 TiDB 集群,用对应旧版本 BR 执行备份,把数据写到 S3 与 GCS 上;
  2. 恢复验证(Test Content):拉起 nightly 版本的 TiDB 集群,用当前目录编译的最新 BR 二进制,逐个恢复历史版本的备份数据,并校验恢复结果符合预期。

下面按这两个阶段结合仓库源码逐一拆解。

阶段一:历史版本备份数据的准备

版本标签(TAG)的自动选取

数据准备的第一步是确定“哪些历史版本”参与测试。核心逻辑在 br/compatibility/get_last_tags.shgetLatestTags 函数中:

# update tags
git fetch --tags

getLatestTags() {
  release_5_branch_regex="^release-5\.[0-9].*$"
  release_4_branch_regex="^release-4\.[0-9].*$"
  TOTAL_TAGS=$(git for-each-ref --sort=creatordate  refs/tags | awk -F '/' '{print $3}')
  # we should filter such tags
  # v5.0.2-20210628
  # v5.0.2-alpha
  # because these tags don't have corresponding docker images.
  filter='-'
  # latest tags
  TAGS=$(echo $TOTAL_TAGS | tr ' ' '\n' | grep -v $filter | tail -n3)
  ...
}

从源码可以看到两个关键设计:

  • 过滤带 - 的标签:如 v5.0.2-20210628v5.0.2-alpha 这类 nightly / alpha 标签没有对应的 docker 镜像,会被剔除;
  • 按当前分支自适应:默认取最新的 3 个 release 标签;如果当前处于 release-5.x 分支,则组合“最近 3 个 v5.x 版本 + 最近 1 个 v4.x 版本”;处于 release-4.x 分支时则只取最近 3 个 v4.x 版本。

这种分支感知的选取策略保证了升级链路的连续性——从 4.x 升级到 5.x 的跨大版本恢复也在覆盖范围内。

逐版本建集群并灌入标准数据

拿到 TAG 列表后,br/compatibility/prepare_backup.sh 负责为每个版本并行拉起一个旧版本集群并执行备份:

runBackup() {
  # generate backup data in /tmp/br/docker/backup_data/$TAG/
  echo "build $1 cluster"
  TAG=$1 PORT_SUFFIX=$2 docker-compose -p $1 -f br/compatibility/backup_cluster.yaml build
  TAG=$1 PORT_SUFFIX=$2 docker-compose -p $1 -f br/compatibility/backup_cluster.yaml up -d
  trap "TAG=$1 PORT_SUFFIX=$2 docker-compose -p $1 -f br/compatibility/backup_cluster.yaml down" EXIT
  # wait for cluster ready
  sleep 20
  # prepare SQL data
  TAG=$1 PORT_SUFFIX=$2 docker-compose -p $1 -f br/compatibility/backup_cluster.yaml exec -T control /go/bin/go-ycsb load mysql -P /prepare_data/workload -p mysql.host=tidb -p mysql.port=4000 -p mysql.user=root -p mysql.db=test
  TAG=$1 PORT_SUFFIX=$2 docker-compose -p $1 -f br/compatibility/backup_cluster.yaml exec -T control br/tests/run_compatible.sh prepare
}

for tag in $TAGS; do
   i=$(( i + 1 ))
   runBackup $tag $i &
done

wait

要点解析:

  • 每个版本一个独立 compose 项目:通过 docker-compose -p $1(以 tag 作为 project 名)与 PORT_SUFFIX=$2 隔离端口,使多个版本的集群可以并行准备,互不干扰;集群模板为 br/compatibility/backup_cluster.yaml

  • 标准数据来自 YCSB:使用 go-ycsb 按 br/compatibility/prepare_data/workload 定义的负载写入 test 库:

    recordcount=1000
    operationcount=0
    workload=core
    
    readallfields=true
    
    readproportion=0
    updateproportion=0
    scanproportion=0
    insertproportion=0
    

    即只加载 1000 条记录到 test.usertable,不做后续读写操作。这个固定的 1000 行规模是后续恢复校验的“期望值”来源;

  • 真正的备份动作在容器内完成run_compatible.sh prepare 会被传入 prepare 参数,进入下一步的存储备份脚本。

S3 / GCS 两套备份脚本

br/tests/run_compatible.sh 通过参数区分准备阶段与运行阶段,prepare 阶段会遍历 br/tests/docker_compatible_*/prepare.sh

  • S3 侧:br/tests/docker_compatible_s3/prepare.sh 先启动/配置 MinIO,再执行备份 SQL:

    # create bucket
    /usr/bin/mc config host add minio http://$S3_ENDPOINT $MINIO_ACCESS_KEY $MINIO_SECRET_KEY
    /usr/bin/mc mb minio/test --ignore-existing
    
    # backup cluster data
    run_sql_in_container "backup database test to 's3://$BUCKET/bk${TAG}?endpoint=http://$S3_ENDPOINT$S3_KEY&force-path-style=true';"
    

    归档目录为 s3://test/bk${TAG},即每个版本一个独立的备份目录;force-path-style=true 是 MinIO 等 S3 兼容服务的典型要求。

  • GCS 侧:br/tests/docker_compatible_gcs/prepare.sh 通过 REST 调用在本地 GCS mock 服务上创建 bucket,再执行:

    run_sql_in_container "backup database test to 'gcs://$BUCKET/bk${TAG}?endpoint=http://$GCS_HOST:$GCS_PORT';"
    
  • 注意这两处使用的都是 BACKUP DATABASE ... TO '...'SQL 语法,即由旧版本 TiDB 内置的 BR 完成备份,保证归档确实出自目标历史版本。

阶段二:用最新 BR 恢复并校验

启动测试集群与构建最新 BR

按照 br/COMPATIBILITY_TEST.md 的标准流程,先基于 docker-compose 拉起 nightly 集群并构建当前目录的 BR:

docker-compose -f docker-compose.yaml rm -s -v && \
docker-compose -f docker-compose.yaml build && \
docker-compose -f docker-compose.yaml up --remove-orphans
docker-compose -f docker-compose.yaml control make compatibility_test

其中 br/docker-compose.yaml 定义了 control、pd0、tikv0/1、minio、gcs(mock)等服务;control 容器把仓库 ./bin 挂载到 /go/src/github.com/pingcap/br/bin,因此容器内 bin/br 就是当前代码目录构建出来的最新 BR,这正是“当前版本 BR 恢复历史数据”的语义来源。在仓库根目录,Makefile 中也提供了对应的本地目标,无需进容器即可触发:

.PHONY: br_compatibility_test_prepare
br_compatibility_test_prepare:
	@cd br && tests/run_compatible.sh prepare

.PHONY: br_compatibility_test
br_compatibility_test:
	@cd br && tests/run_compatible.sh run

run_compatible.sh:测试入口与变量约定

所有兼容测试的统一入口是 br/tests/run_compatible.sh

source ${BASH_SOURCE[0]%/*}/../compatibility/get_last_tags.sh
getLatestTags
echo "start test on $TAGS"

EXPECTED_KVS=1000
PD_ADDR="pd0:2379"
GCS_HOST="gcs"
GCS_PORT="20818"
TEST_DIR=/tmp/backup_restore_compatibility_test
mkdir -p "$TEST_DIR"
rm -f "$TEST_DIR"/*.log &> /dev/null

for script in br/tests/docker_compatible_*/${1}.sh; do
    echo "*===== Running test $script... =====*"
    TEST_DIR="$TEST_DIR" \
    PD_ADDR="$PD_ADDR" \
    GCS_HOST="$GCS_HOST" \
    GCS_PORT="$GCS_PORT" \
    TAGS="$TAGS" \
    EXPECTED_KVS="$EXPECTED_KVS" \
    PATH="br/tests/_utils:bin:$PATH" \
    TEST_NAME="$(basename "$(dirname "$script")")" \
    BR_LOG_TO_TERM=1 \
    bash "$script"
done

这段脚本体现了测试框架的几个约定:

  • 参数 ${1} 决定阶段:传 prepare 时执行各存储目录下的 prepare.sh,传 run 时执行 _run.sh(下划线前缀使其在 prepare 阶段不会被误匹配);
  • 环境变量注入TAGS(待测版本列表)、EXPECTED_KVS=1000(期望行数,与 YCSB recordcount 一致)、PD_ADDR=pd0:2379(容器内 PD 地址)、GCS mock 的 host/port;
  • BR_LOG_TO_TERM=1 使 BR 日志输出到终端,便于 CI 中排查失败原因;
  • run_sql_in_container 等辅助函数来自 br/tests/_utils 下的共享脚本(如 br/tests/br_test_utils.sh),通过 PATH 注入到子脚本中。

S3 恢复脚本:命令与校验逻辑

br/tests/docker_compatible_s3/_run.sh 是“恢复 + 校验”的完整范例:

BUCKET="test"
MINIO_ACCESS_KEY='brs3accesskey'
MINIO_SECRET_KEY='brs3secretkey'
S3_ENDPOINT=minio:24927
S3_KEY="&access-key=$MINIO_ACCESS_KEY&secret-access-key=$MINIO_SECRET_KEY"

# restore backup data one by one
for TAG in ${TAGS}; do
    echo "restore ${TAG} data starts..."
    # after BR merged into TiDB we need skip version check because the build from tidb is not a release version.
    bin/br restore db --db test -s "s3://$BUCKET/bk${TAG}?endpoint=http://$S3_ENDPOINT$S3_KEY" --pd $PD_ADDR --check-requirements=false
    row_count=$(run_sql_in_container  "SELECT COUNT(*) FROM test.usertable;" | awk '/COUNT/{print $2}')
    if [ $row_count != $EXPECTED_KVS ]; then
       echo "restore kv count is not as expected(1000), obtain $row_count"
       exit 1
    fi
    # clean up data for next restoration
    run_sql_in_container "drop database test;"
done

逐条说明:

  • bin/br restore db --db test:使用 CLI 方式(而非 SQL)恢复 test 库,-s 指向版本化目录 s3://test/bk${TAG},endpoint 与访问密钥直接拼在 URI 查询参数中,这是 BR 支持的 URI 内嵌凭证方式;
  • --check-requirements=false:脚本注释解释了原因——BR 并入 TiDB 仓库后,nightly 构建不是正式 release 版本,默认的版本要求检查(check requirements)会将其拦截,因此在兼容测试中显式跳过;
  • 数据正确性校验:恢复后执行 SELECT COUNT(*) FROM test.usertable,行数必须等于 EXPECTED_KVS(1000),否则立即 exit 1 使整个测试失败;
  • 循环间清理drop database test 保证下一个版本在干净环境上恢复,避免跨版本数据互相污染。

GCS 恢复脚本:额外的 OAuth 凭证细节

br/tests/docker_compatible_gcs/_run.sh 与 S3 版结构一致,差异在于 GCS 客户端需要 service account 凭证。脚本内嵌了一段 service_account JSON(token_uri 指向 compose 网络中的 oauth mock 服务 http://oauth:5000/oauth/token),保存后导出环境变量:

# save CREDENTIALS to file
echo $KEY > "br/tests/$TEST_NAME/config.json"

# export test CREDENTIALS for gcs oauth
export GOOGLE_APPLICATION_CREDENTIALS="br/tests/$TEST_NAME/config.json"

# restore backup data one by one
for TAG in ${TAGS}; do
    bin/br restore db --db test -s "gcs://$BUCKET/bk${TAG}" --pd $PD_ADDR --gcs.endpoint="http://$GCS_HOST:$GCS_PORT" --check-requirements=false
    ...
done

这里的 --gcs.endpoint 把 GCS 客户端请求重定向到本地 mock(默认 gcs:20818),使测试完全离线可复现。

运行方式汇总与注意事项

综合文档与仓库脚本,完整运行路径如下:

  1. 准备阶段(生成 6 份历史版本备份归档):

    # 仓库根目录
    make br_compatibility_test_prepare
    # 等价于 cd br && tests/run_compatible.sh prepare
    

    或手动执行 br/compatibility/prepare_backup.sh,它会并行拉起各历史版本集群、灌入 YCSB 数据并触发 S3/GCS 备份。

  2. 验证阶段(nightly 集群 + 最新 BR 逐个恢复):

    # 仓库根目录
    make br_compatibility_test
    # 等价于 cd br && tests/run_compatible.sh run
    

    或在容器内按 br/COMPATIBILITY_TEST.md 给出的方式执行:

    docker-compose -f docker-compose.yaml rm -s -v && \
    docker-compose -f docker-compose.yaml build && \
    docker-compose -f docker-compose.yaml up --remove-orphans
    
    docker-compose -f docker-compose.yaml control make compatibility_test
    

运行时的几个前提与限制值得注意:

  • 依赖 git tag:版本选取依赖 git fetch --tags 拉取到的 release 标签,本地浅克隆或未同步 tag 的环境会得到错误的 TAGS 列表;脚本中内置的正则仍面向 4.x/5.x 分支(见 br/compatibility/get_last_tags.sh),在更新的大版本分支上使用时,从源码结构看该逻辑可能需要按新的 release 分支正则调整;
  • 备份数据不可再生:准备阶段用的是历史版本 TiDB/BR,若归档缺失,无法用新版本重新生成语义等价的“旧版本备份”,这是该工作流必须与 CI 定时任务配合的原因;
  • 校验口径:当前校验是行数一致性(COUNT(*) 等于 1000),属于快速而确定性的正确性断言,配合“逐版本恢复 + 恢复后清库”的隔离策略,构成最小完备的向后兼容信号;
  • --check-requirements=false 只应用于测试场景:它绕过了版本要求检查,生产环境升级恢复时不应照搬该参数。

小结

BR 兼容测试用一条清晰的流水线回答“旧备份能否被新 BR 恢复”:getLatestTags 按分支选取最近 3 个 release 版本 → prepare_backup.sh 并行拉起旧版本集群,YCSB 写入固定 1000 行数据后经 SQL 备份到 MinIO 与 mock GCS → run_compatible.sh run 驱动各存储目录的 _run.sh,用当前构建的 BR 执行 restore db,以 COUNT(*) 断言校验并清库。整套流程全部由 br/tests/run_compatible.shbr/compatibility/br/tests/docker_compatible_s3br/tests/docker_compatible_gcs 目录下的脚本构成,配合 Makefilebr_compatibility_test_prepare / br_compatibility_test 目标即可在本地或 CI 中完整复现。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384