0
0
0
0
专栏/.../

两种 TiDB 备份方案任你选择:NFS or S3(内含操作实践/备份/恢复)

 HZL_0-0  发表于  2025-09-05

备份

概述

备份方式

TiDB 支持两种类型的备份,全量备份包含集群某个时间点的全量数据,日志备份包含业务写入在 TiDB 产生的数据变更记录。推荐这两种备份方式一起使用:

  • 开启日志备份:运行 tiup br log start 命令来启动日志备份任务,任务会在每个 TiKV 节点上持续运行,以小批量的形式定期将 TiDB 变更数据备份到指定存储中。
  • 定期执行快照(全量)备份:运行 tiup br backup full 命令来备份集群快照到备份存储,例如在每天零点进行集群快照备份。

备份存储

TiDB 集群部署在自建机房中,则推荐以下方式:

  • 搭建 MinIO 作为备份存储系统,使用 S3 协议将数据备份到 MinIO 中。
  • 挂载 NFS(如 NAS)盘到 br 工具和所有的 TiKV 实例,使用 POSIX file system 接口将备份数据写入对应的 NFS 目录中。

注意:

如果没有挂载 NFS 到 br 工具或 TiKV 节点,或者使用了支持 S3、GCS 或 Azure Blob Storage 协议的远端存储,那么 br 工具备份的数据会在各个 TiKV 节点生成。注意这不是推荐的 br 工具使用方式,因为备份数据会分散在各个节点的本地文件系统中,聚集这些备份数据可能会造成数据冗余和运维上的麻烦,而且在不聚集这些数据便直接恢复的时候会遇到 SST file not found 报错。

NFS 方式(不推荐)

NFS 快照备份实践

备份存储配置

集群环境中TiKV部署在87、88、89,为了使得备份不分散3台服务器上,这里使用NFS将3台TiKV的备份数据聚集到87这一台服务器的一个目录中。

暂时无法在飞书文档外展示此内容

  1. 在87、88、89上安装nfs并启动
systemctl status nfs-server

  1. 在87上执行以下命令:
mkdir /tidb/backup
echo "/tidb/backup *(rw,sync,no_root_squash)" >> /etc/exports
systemctl enable nfs-server
systemctl restart nfs-server
exportfs -r
showmount -e 172.23.57.87

  1. 将88和89的备份目录挂载到87的共享目录上,在88、89上分别执行以下命令:
mkdir /tidb/backup
echo "172.23.57.87:/tidb/backup /tidb/backup nfs rw,timeo=7,hard,intr,bg,suid,lock 0 0" >> /etc/fstab
sudo mount -t nfs -o rw 172.23.57.87:/tidb/backup /tidb/backup
df -h
# 在87、88、89执行授权命令
chown -R tidb:tidb /tidb/backup
chmod -R 775 /tidb/backup

备份工具br安装

  1. 在87上使用tidb用户执行:
tiup install br

执行全量备份

  1. 在87上使用tidb用户执行:
tiup br backup full --pd "172.23.57.87:2379" \
    --storage "local:///tidb/backup/full20240914" \
    --log-file backup.log.full20240914 \
    --ratelimit 128

NFS 快照备份恢复实践

模拟删除test库

drop database test

恢复全量备份数据

  1. 在87上使用tidb用户执行:
tiup br restore full --pd "172.23.57.87:2379" \
--storage "local:///tidb/backup/full20240914" \
--ratelimit 128 \
--log-file restorefull.log.full20240914

NFS 定时快照备份

准备备份脚本

  1. 在87上创建定时备份脚本:
#!/bin/bash
export DATEDIR=`date +%Y%m%d`
export BASEDIR=/tidb/backup
mkdir $BASEDIR/$DATEDIR
chown -R tidb:tidb $BASEDIR/$DATEDIR
chmod -R 777 $BASEDIR/$DATEDIR
su - tidb <<EOF
tiup br backup full --pd "172.23.57.87:2379" --storage "local://$BASEDIR/$DATEDIR" --ratelimit 128 --log-file $BASEDIR/$DATEDIR/fullbackup_`date +%Y%m%d`.log
sync
sleep 10
find ${BASEDIR} -type f -mtime +31 -exec rm {} \;
find ${BASEDIR} -type d -empty -delete

添加定时任务

  1. 添加定时任务

使用 crontab -e 命令添加定时任务

# 添加 br 备份任务
0 2 * * * sh /opt/scripts/fulldbbak.sh

0 2 * * *表示每天凌晨2点进行备份, sh /opt/scripts/fulldbbak.sh表示执行备份脚本。

Minio 方式(推荐)

Minio 快照备份实践

离线部署 Minio

这里远程备份文件对象存储选择 minio,先使用docker在172.23.57.233上部署minio

  1. 下载minio镜像
  1. 加载镜像
docker load -i /opt/minio_minio-amd64.tar
  1. 编写 docker-compose 文件
version: '3'
services:
  minio:
    image: minio/minio:latest
    container_name: minio
    restart: always
    environment:
      MINIO_ACCESS_KEY: admin
      MINIO_SECRET_KEY: admin123456
    ports:
      - "9000:9000"
      - "9090:9090"
    volumes:
      - /opt/minio_data:/data
    command: server /data --console-address ":9090" --address ":9000"
  1. 运行 minio
docker-compose -f /opt/docker-compose-minio.yaml up -d
  1. 测试 minio

访问 http://172.23.57.233:9090/login 页面,使用设定的账户密码登录来测试是否部署成功

创建一个 bucket。

创建一个Region,名称为 us-east-1

重启 minio 容器。

备份工具br安装

  1. 在87上使用tidb用户执行:
tiup install br

执行全量备份

  1. 在87上使用tidb用户执行:
tiup br backup full --pd "172.23.57.87:2379,172.23.57.88:2379,172.23.57.89:2379" \
    --storage "s3://tidb-backup/backup-20250317?access-key=admin&secret-access-key=admin123456" \
    --s3.endpoint "http://172.23.57.233:9000" \
    --log-file backup.log.full20250317 \
    --ratelimit 128

Minio 快照备份恢复实践

模拟删除test库

drop database test

恢复全量备份数据

  1. 在87上使用tidb用户执行:
tiup br restore full --pd "172.23.57.87:2379,172.23.57.88:2379,172.23.57.89:2379" \
--storage "s3://tidb-backup/backup-20250317?access-key=admin&secret-access-key=admin123456" \
--s3.endpoint "http://172.23.57.233:9000" \
--ratelimit 128 \
--log-file restorefull.log.full20250317

Minio 定时快照备份

准备备份脚本

  1. 172.23.57.87 上创建定时备份脚本,执行日志存在 /tidb/minio_backup_log 目录下:
#!/bin/bash
export DATEDIR=`date +%Y%m%d`
export BASEDIR=/tidb/minio_backup_log
export MINIO_BASEDIR=tidb-backup
export PD=172.23.57.87:2379,172.23.57.88:2379,172.23.57.89:2379
export MINIO_USERNAME=admin
export MINIO_PASSWORD=admin123456
export MINIO_IP=172.23.57.233
export MINIO_PORT=9000
mkdir $BASEDIR/$DATEDIR
chown -R tidb:tidb $BASEDIR/$DATEDIR
chmod -R 777 $BASEDIR/$DATEDIR
su - tidb <<EOF
tiup br backup full --pd "$PD" --storage "s3://$MINIO_BASEDIR/TiDB_$DATEDIR?access-key=$MINIO_USERNAME&secret-access-key=$MINIO_PASSWORD" --s3.endpoint "http://$MINIO_IP:$MINIO_PORT" --ratelimit 128 --log-file $BASEDIR/$DATEDIR/fullbackup_`date +%Y%m%d`.log
sync
sleep 20
find ${BASEDIR} -type f -mtime +15 -exec rm {} \;
find ${BASEDIR} -type d -empty -delete

添加定时任务

  1. 添加定时任务

使用 crontab -e 命令添加定时任务

# 每天凌晨2点执行 br 备份任务   【0 3 1 * *   表示每个月1号凌晨3点的cron表达式】
0 2 * * * sh /opt/scripts/minio_fulldbback.sh

Python 脚本定时清理minio备份文件

在服务器 172.23.57.87/opt/scripts 目录下创建执行脚本。

Python 脚本准备

要求只保留 minio 中 TiDB 5天的备份数据。

  1. 编写脚本,修改对应的Minio配置目标配置
  2. 脚本首行添加 Python 路径(通过 which python3 查看)
  3. 增加可执行权限:chmod +x /opt/scripts/clean_minio_backup.py
#!/opt/anaconda3/bin/python3

import os
from datetime import datetime, timedelta
from minio import Minio
from minio.error import S3Error

# MinIO配置
MINIO_ENDPOINT = '172.23.57.233:9000'
MINIO_ACCESS_KEY = 'admin'
MINIO_SECRET_KEY = 'admin123456'
MINIO_SECURE = False  # True for HTTPS

# 目标配置
BUCKET_NAME = 'tidb-backup'
PARENT_DIR = ''  # 必须以'/'结尾
DATE_FORMAT = '%Y%m%d'  # 文件夹名中的日期格式
DAYS_TO_KEEP = 5  # 保留天数


def initialize_minio_client():
    """初始化MinIO客户端"""
    return Minio(
        MINIO_ENDPOINT,
        access_key=MINIO_ACCESS_KEY,
        secret_key=MINIO_SECRET_KEY,
        secure=MINIO_SECURE
    )


def extract_date_from_folder(folder_name):
    """
    从文件夹名中提取日期
    格式应为: xxx_YYYYMMDD 或 YYYYMMDD_xxx
    """
    try:
        # 尝试从常见格式中提取日期部分
        for part in folder_name.split('_'):
            if len(part) == 8 and part.isdigit():  # YYYYMMDD格式
                return datetime.strptime(part, DATE_FORMAT)
        return None
    except ValueError:
        return None


def get_subfolders(minio_client):
    """获取指定目录下的所有子文件夹"""
    folders = set()

    try:
        # 非递归列出对象,只获取直接子项
        objects = minio_client.list_objects(
            BUCKET_NAME,
            prefix=PARENT_DIR,
            recursive=False
        )

        for obj in objects:
            # 获取相对于父目录的路径
            relative_path = obj.object_name[len(PARENT_DIR):]

            # 提取第一级子目录名
            if '/' in relative_path:
                folder_name = relative_path.split('/')[0]
                if folder_name:  # 确保不是空字符串
                    folders.add(folder_name)
    except S3Error as err:
        print(f"MinIO错误: {err}")

    return folders


def clean_old_folders(minio_client):
    """清理超过保留天数的旧文件夹"""
    cutoff_date = datetime.now() - timedelta(days=DAYS_TO_KEEP)
    folders = get_subfolders(minio_client)

    deleted_count = 0
    kept_count = 0

    for folder in folders:
        folder_date = extract_date_from_folder(folder)

        if not folder_date:
            print(f"跳过不符合命名格式的文件夹: {folder}")
            continue

        if folder_date < cutoff_date:
            # 删除整个文件夹及其内容
            folder_path = os.path.join(PARENT_DIR, folder) + '/'
            print(f"删除文件夹: {folder_path} (日期: {folder_date.strftime('%Y-%m-%d')})")

            try:
                # 递归列出文件夹下所有对象
                objects_to_delete = minio_client.list_objects(
                    BUCKET_NAME,
                    prefix=folder_path,
                    recursive=True
                )

                # 删除所有对象
                for obj in objects_to_delete:
                    minio_client.remove_object(BUCKET_NAME, obj.object_name)

                deleted_count += 1
            except S3Error as err:
                print(f"删除文件夹失败 {folder_path}: {err}")
        else:
            kept_count += 1
            print(f"保留文件夹: {folder} (日期: {folder_date.strftime('%Y-%m-%d')})")

    print(f"\n操作完成: 删除 {deleted_count} 个旧文件夹, 保留 {kept_count} 个文件夹")


if __name__ == "__main__":
    client = initialize_minio_client()
    clean_old_folders(client)

使用 Linux 中 Crontab (定时任务)进行清理

crontab -e

添加下面命令后保存退出:

# 每天凌晨3点执行清理命令 0 3 * * * /opt/scripts/clean_minio_backup.py >> /opt/scripts/clean.log 2>&1

0
0
0
0

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

评论
暂无评论