备份
概述
备份方式
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这一台服务器的一个目录中。
暂时无法在飞书文档外展示此内容
- 在87、88、89上安装nfs并启动
systemctl status nfs-server
- 在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
- 将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安装
- 在87上使用tidb用户执行:
tiup install br
执行全量备份
- 在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
恢复全量备份数据
- 在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 定时快照备份
准备备份脚本
- 在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
添加定时任务
- 添加定时任务
使用 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。
- 下载minio镜像
- 加载镜像
docker load -i /opt/minio_minio-amd64.tar
- 编写 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"
- 运行 minio
docker-compose -f /opt/docker-compose-minio.yaml up -d
- 测试 minio
访问 http://172.23.57.233:9090/login 页面,使用设定的账户密码登录来测试是否部署成功
创建一个 bucket。
创建一个Region,名称为 us-east-1
重启 minio 容器。
备份工具br安装
- 在87上使用tidb用户执行:
tiup install br
执行全量备份
- 在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
恢复全量备份数据
- 在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 定时快照备份
准备备份脚本
- 在 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
添加定时任务
- 添加定时任务
使用 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天的备份数据。
- 编写脚本,修改对应的Minio配置和目标配置
- 脚本首行添加 Python 路径(通过
which python3
查看) - 增加可执行权限: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