给数据买份保险 —— 备份与恢复实战
上一章我们学会了用主从复制给数据创建”分身”,但复制不是备份。如果有人误执行了一条
DELETE FROM orders(没有WHERE条件),这条灾难性的SQL会被忠实地复制到所有从库——分身们一起”阵亡”。真正的保险是备份:一份和线上系统解耦的、独立的、可以回溯到任意时间点的数据快照。
📋 开篇自测:你已经知道多少?
- 逻辑备份和物理备份各指什么?各有什么优缺点?
- mysqldump的
--single-transaction参数起什么作用?为什么对InnoDB表很重要?- 什么是基于时间点的恢复(PITR)?它依赖哪两样东西?
- 备份文件做完之后,如何验证这份备份是可用的?
一、备份策略概述——保险方案的设计
在动手备份之前,先理清几个核心概念。
全量备份、增量备份与差异备份
这三种备份策略就像三种不同的”拍照”方式:
周日 周一 周二 周三 周四
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│全量 │ │ 变化1 │ │ 变化2 │ │ 变化3 │ │ 变化4 │
│快照 │ │ │ │ │ │ │ │ │
└──────┘ └──────┘ └──────┘ └──────┘ └──────┘
全量备份:每次都完整拍一张照片
周日[全量] → 周一[全量] → 周二[全量] → ...
优点:恢复简单(直接用最近的全量)
缺点:耗时、占空间
增量备份:只拍"和上一次备份相比变化了的部分"
周日[全量] → 周一[增量:变化1] → 周二[增量:变化2] → 周三[增量:变化3]
恢复周三数据 = 周日全量 + 周一增量 + 周二增量 + 周三增量
优点:速度快、省空间
缺点:恢复链条长,任一环节损坏则无法恢复
差异备份:只拍"和最近一次全量备份相比变化了的部分"
周日[全量] → 周一[差异:变化1] → 周二[差异:变化1+2] → 周三[差异:变化1+2+3]
恢复周三数据 = 周日全量 + 周三差异
优点:恢复只需全量+最新差异,比增量简单
缺点:差异文件随时间增长
逻辑备份 vs 物理备份
| 维度 | 逻辑备份 | 物理备份 |
|---|---|---|
| 备份内容 | SQL语句或CSV数据 | 原始数据文件(.ibd等) |
| 代表工具 | mysqldump、mysqlpump | XtraBackup、文件系统快照 |
| 备份速度 | 慢(需要读取并转换数据) | 快(直接拷贝文件) |
| 恢复速度 | 慢(需要逐条执行SQL) | 快(直接替换文件) |
| 跨版本 | 支持(SQL是通用的) | 不支持(文件格式可能不兼容) |
| 跨平台 | 支持 | 有限制 |
| 备份粒度 | 可以精确到单表 | 通常是整个实例或库 |
| 适用数据量 | 中小型(GB级别) | 大型(TB级别) |
🤔 想一想 你的业务数据库有多大?如果用逻辑备份需要多长时间?物理备份呢?你会为你的业务选择什么样的备份策略组合?
二、mysqldump——最可靠的老朋友
mysqldump是MySQL自带的逻辑备份工具,虽然速度不是最快,但胜在简单可靠、无需额外安装。
基本用法
# 备份单个数据库
mysqldump -u root -p --databases ticket_system > ticket_system_full.sql
# 备份多个数据库
mysqldump -u root -p --databases ticket_system warehouse_db > multi_db.sql
# 备份所有数据库
mysqldump -u root -p --all-databases > all_databases.sql
# 只备份特定表
mysqldump -u root -p ticket_system flight_records passenger_info > tables_backup.sql
# 只备份表结构,不要数据
mysqldump -u root -p --no-data ticket_system > schema_only.sql
# 只备份数据,不要建表语句
mysqldump -u root -p --no-create-info ticket_system > data_only.sql
关键参数详解
mysqldump -u root -p \
--single-transaction \ # 【重要】InnoDB表一致性备份的关键
--routines \ # 包含存储过程和函数
--triggers \ # 包含触发器(默认开启)
--events \ # 包含定时事件
--set-gtid-purged=AUTO \ # GTID环境下的处理方式
--source-data=2 \ # 在备份中记录binlog位置(注释形式)
# MySQL 8.0.26 之前的版本使用 --master-data=2
--flush-logs \ # 备份前刷新binlog,便于后续增量恢复
--databases ticket_system \
> ticket_backup_$(date +%Y%m%d_%H%M%S).sql
几个最重要的参数:
--single-transaction
这个参数的作用是在备份开始前开启一个长事务,利用MVCC的快照读来获取一致性视图。整个备份过程中读到的数据都是事务开始那一刻的状态,不会受到备份期间其他写操作的影响。对InnoDB表来说,这个参数是必须的。
不用这个参数会怎样?mysqldump会对每张表加LOCK TABLES读锁,备份期间所有写操作都会被阻塞。对线上业务来说这是不可接受的。
--source-data(MySQL 8.0.26+,旧版使用 --master-data)
在备份文件开头记录备份时刻的binlog文件名和位置。这在做PITR(基于时间点恢复)时非常关键。
--source-data=1:记录为可执行的CHANGE REPLICATION SOURCE TO语句--source-data=2:记录为注释(推荐,避免恢复时意外执行)
-- 备份文件中的记录(--source-data=2的效果)
-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000047', MASTER_LOG_POS=154;
--set-gtid-purged
在开启了GTID的环境中控制备份文件里的SET @@GLOBAL.gtid_purged语句。恢复到一个空实例时用AUTO或ON,恢复到已有数据的实例时用OFF。
备份文件里有什么?
# 查看备份文件的前50行,了解结构
head -50 ticket_backup_20260312.sql
一个典型的mysqldump输出文件包含:
-- MySQL dump 10.13 Distrib 8.0.36
-- Server version 8.0.36
-- 设置环境变量(字符集、外键检查等)
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 创建数据库
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `ticket_system`;
USE `ticket_system`;
-- 删除旧表(如果存在)
DROP TABLE IF EXISTS `flight_records`;
-- 建表语句
CREATE TABLE `flight_records` (
`record_id` int NOT NULL AUTO_INCREMENT,
`flight_no` varchar(20) NOT NULL,
`departure_city` varchar(50) NOT NULL,
`arrival_city` varchar(50) NOT NULL,
`departure_time` datetime NOT NULL,
`price` decimal(10,2) NOT NULL,
PRIMARY KEY (`record_id`),
KEY `idx_flight_no` (`flight_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 数据(INSERT语句)
INSERT INTO `flight_records` VALUES
(1,'CA1234','北京','上海','2026-03-15 08:00:00',980.00),
(2,'MU5678','广州','成都','2026-03-15 10:30:00',1250.00),
...
-- 恢复环境变量
SET FOREIGN_KEY_CHECKS = 1;
恢复数据
# 从备份文件恢复
mysql -u root -p < ticket_backup_20260312.sql
# 恢复到指定数据库(如果备份文件中不包含CREATE DATABASE)
mysql -u root -p ticket_system < ticket_backup_20260312.sql
# 大文件恢复时显示进度(借助pv工具)
pv ticket_backup_20260312.sql | mysql -u root -p
# 恢复前建议关闭一些检查以加速
mysql -u root -p -e "
SET GLOBAL innodb_flush_log_at_trx_commit = 2;
SET GLOBAL sync_binlog = 0;
"
# 恢复完成后改回安全设置
mysql -u root -p -e "
SET GLOBAL innodb_flush_log_at_trx_commit = 1;
SET GLOBAL sync_binlog = 1;
"
⚠️ 常见误区 误区:mysqldump备份的文件可以直接用文本编辑器打开修改后再恢复。 对于小文件没问题,但GB级别的备份文件用文本编辑器打开会导致内存耗尽。如果需要从备份中提取特定表的数据,用
sed或awk等流式工具更安全。更好的做法是备份时就按表拆分。
三、XtraBackup——大库的救星
当数据库达到几十GB甚至TB级别时,mysqldump的速度就捉襟见肘了。Percona XtraBackup是业界最流行的MySQL物理备份工具。注意版本兼容性:Percona XtraBackup 8.0 仅支持 MySQL 8.0,不支持 MySQL 5.7;如果需要备份 MySQL 5.7,应使用 XtraBackup 2.4。
热备原理
XtraBackup的精妙之处在于它能在不锁表的情况下完成物理备份。原理如下:
1. 启动备份 → 开始拷贝InnoDB数据文件(.ibd)
2. 拷贝过程中有新的写入?没关系,同时监控redo日志
3. 数据文件拷贝完成 → 拷贝备份期间产生的redo日志
4. 短暂加锁(FTWRL) → 拷贝非InnoDB表和binlog位置信息
5. 释放锁 → 备份完成
恢复(prepare)阶段:
将备份期间的redo日志应用到数据文件 → 得到一致性快照
这个思路和InnoDB崩溃恢复的原理异曲同工:先拿到数据文件,再用日志把数据”追平”到一致的状态。
全量备份与恢复
# 全量备份
xtrabackup --backup \
--user=root \
--password=xxx \
--target-dir=/backup/full_20260312
# 准备备份(apply redo log,使数据一致)
xtrabackup --prepare \
--target-dir=/backup/full_20260312
# 恢复(需要先停止MySQL并清空数据目录)
systemctl stop mysqld
rm -rf /var/lib/mysql/*
xtrabackup --copy-back \
--target-dir=/backup/full_20260312
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld
增量备份
XtraBackup原生支持增量备份,只备份自上次备份以来发生变化的数据页。
# 第一步:做一个全量备份作为基础
xtrabackup --backup \
--user=root --password=xxx \
--target-dir=/backup/base
# 第二步:基于全量做第一次增量备份
xtrabackup --backup \
--user=root --password=xxx \
--target-dir=/backup/incr_1 \
--incremental-basedir=/backup/base
# 第三步:基于第一次增量做第二次增量备份
xtrabackup --backup \
--user=root --password=xxx \
--target-dir=/backup/incr_2 \
--incremental-basedir=/backup/incr_1
恢复增量备份需要按顺序”叠加”:
# 准备全量备份(注意:使用--apply-log-only,不回滚未提交事务)
xtrabackup --prepare --apply-log-only \
--target-dir=/backup/base
# 叠加第一次增量
xtrabackup --prepare --apply-log-only \
--target-dir=/backup/base \
--incremental-dir=/backup/incr_1
# 叠加最后一次增量(最后一次不加--apply-log-only)
xtrabackup --prepare \
--target-dir=/backup/base \
--incremental-dir=/backup/incr_2
# 恢复
systemctl stop mysqld
rm -rf /var/lib/mysql/*
xtrabackup --copy-back --target-dir=/backup/base
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld
增量备份链的关系:
/backup/base ← 周日全量(所有数据)
/backup/incr_1 ← 周一增量(只有周一变化的页)
/backup/incr_2 ← 周二增量(只有周二变化的页)
恢复周二数据 = base + incr_1 + incr_2(按顺序叠加)
🤔 想一想 如果增量备份链中的
incr_1文件损坏了,incr_2还能用吗?这对备份策略的设计有什么启示?
四、binlog恢复——时间机器
全量备份 + binlog = 任意时间点恢复(PITR, Point-In-Time Recovery)。
场景还原:周二上午10:15,有人误执行了DROP TABLE flight_records。你手上有周日凌晨的全量备份和之后的binlog。恢复步骤如下:
时间线:
周日03:00 周一... 周二10:15 周二10:20
↓ ↓ ↓
[全量备份] [正常写入...] [误删表!] [发现问题]
恢复目标:恢复到周二10:14的状态(误删之前)
第一步:找到全量备份记录的binlog位置
# 如果用mysqldump备份(带--source-data=2)
grep "CHANGE MASTER\|CHANGE REPLICATION SOURCE" ticket_backup_20260308.sql
# 输出:-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000045', MASTER_LOG_POS=154;
# 如果用XtraBackup
cat /backup/base/xtrabackup_binlog_info
# 输出:binlog.000045 154
第二步:恢复全量备份到一个临时实例
# 创建临时恢复目录
mkdir -p /tmp/mysql_recovery
# 如果是mysqldump备份
mysql -u root -p --port=3307 < ticket_backup_20260308.sql
# 如果是XtraBackup备份
# (步骤同前面XtraBackup恢复章节)
第三步:用mysqlbinlog回放binlog到误操作之前
# 先查看binlog,找到误操作的确切位置
mysqlbinlog --start-datetime='2026-03-10 10:00:00' \
--stop-datetime='2026-03-10 10:20:00' \
/var/lib/mysql/binlog.000047 | grep -B 5 "DROP TABLE"
# 假设找到DROP TABLE事件在位置 438562
# 回放binlog:从全量备份位置开始,到误操作之前停止
mysqlbinlog --start-position=154 /var/lib/mysql/binlog.000045 \
/var/lib/mysql/binlog.000046 \
| mysql -u root -p --port=3307
# 注意:--start-position 只对第一个指定的日志文件生效,后续文件从头开始读取
mysqlbinlog --stop-position=438562 /var/lib/mysql/binlog.000047 \
| mysql -u root -p --port=3307
也可以用时间来精确控制:
# 用时间戳控制恢复范围
mysqlbinlog --start-datetime='2026-03-08 03:00:00' \
--stop-datetime='2026-03-10 10:14:59' \
/var/lib/mysql/binlog.000045 \
/var/lib/mysql/binlog.000046 \
/var/lib/mysql/binlog.000047 \
| mysql -u root -p --port=3307
第四步:验证并导回生产环境
# 在临时实例上验证数据是否正确
mysql -u root -p --port=3307 -e "
SELECT COUNT(*) FROM ticket_system.flight_records;
SELECT * FROM ticket_system.flight_records ORDER BY record_id DESC LIMIT 5;
"
# 确认无误后,将恢复的表导回生产库
mysqldump -u root -p --port=3307 ticket_system flight_records \
| mysql -u root -p --port=3306 ticket_system
⚠️ 常见误区 误区:有了binlog就不需要全量备份。 binlog回放是从某个起点开始”重放”所有变更操作。如果没有全量备份作为起点,你需要从数据库创建之初的第一条binlog开始回放——这可能需要几天甚至几周的时间,而且早期的binlog很可能已经被清理掉了。全量备份是PITR的起跑线,两者缺一不可。
五、备份验证——保险单到底能不能赔
一份从未验证过的备份,和没有备份几乎一样危险。太多团队在真正需要恢复数据时才发现备份文件是损坏的或不完整的。
验证mysqldump备份
# 基本检查:文件是否完整(应以特定注释结尾)
tail -5 ticket_backup_20260312.sql
# 正常的结尾应该包含:
# -- Dump completed on 2026-03-12 03:00:15
# 文件大小检查(和历史备份对比,大幅缩小可能有问题)
ls -lh ticket_backup_20260312.sql
# 与上一次备份对比
ls -lh ticket_backup_20260305.sql
# 完整性验证:恢复到测试实例
mysql -u root -p --port=3307 < ticket_backup_20260312.sql
# 验证关键表的行数
mysql -u root -p --port=3307 -e "
SELECT 'flight_records' AS tbl, COUNT(*) AS cnt FROM ticket_system.flight_records
UNION ALL
SELECT 'passenger_info', COUNT(*) FROM ticket_system.passenger_info
UNION ALL
SELECT 'booking_orders', COUNT(*) FROM ticket_system.booking_orders;
"
验证XtraBackup备份
# XtraBackup的prepare过程本身就是验证
# 如果prepare成功,说明备份数据是一致的
xtrabackup --prepare --target-dir=/backup/full_20260312
# 检查输出中是否有 "completed OK!" 字样
# 完整验证:恢复到测试实例并检查数据
定期恢复演练
最靠谱的验证方式是定期做恢复演练。建议制度化:
每月一次:
1. 从最新备份恢复到测试环境
2. 验证关键表数据行数与生产一致
3. 执行几条业务查询,确认数据准确
4. 测试PITR流程(恢复到指定时间点)
5. 记录恢复耗时
每季度一次:
1. 模拟主库故障场景
2. 完整走一遍"全量备份 + binlog回放"流程
3. 计算RPO(恢复点目标)和RTO(恢复时间目标)
4. 更新恢复操作手册
六、自动化备份脚本——让机器来守夜
手动备份不可持续。下面是一个生产级备份脚本的框架。
mysqldump自动化脚本
#!/bin/bash
# mysql_backup.sh — 每日自动备份脚本
# ========== 配置区 ==========
BACKUP_DIR="/data/backups/mysql"
MYSQL_USER="backup_user"
MYSQL_PASS="backup_pass_here" # ⚠️ 安全警告:命令行传递密码会被MySQL警告,也可能泄露到进程列表中
# 生产环境务必使用 mysql_config_editor 创建 .mylogin.cnf 或通过环境变量替代明文密码
MYSQL_HOST="127.0.0.1"
DATABASES="ticket_system warehouse_db"
RETENTION_DAYS=15 # 备份保留天数
DATE_TAG=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${BACKUP_DIR}/backup_${DATE_TAG}.log"
# ========== 函数定义 ==========
log_msg() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
check_result() {
if [ $? -eq 0 ]; then
log_msg "SUCCESS: $1"
else
log_msg "FAILED: $1"
# 发送告警(替换为你的告警方式)
curl -s -X POST "https://hooks.your-alert.com/notify" \
-d "message=MySQL备份失败: $1" > /dev/null 2>&1
exit 1
fi
}
# ========== 准备工作 ==========
mkdir -p "$BACKUP_DIR"
# ========== 执行备份 ==========
log_msg "开始备份..."
BACKUP_FILE="${BACKUP_DIR}/full_backup_${DATE_TAG}.sql.gz"
# 注意:--source-data 是 MySQL 8.0.26+ 的参数名,8.0.26 之前的版本使用 --master-data=2
mysqldump \
-h "$MYSQL_HOST" \
-u "$MYSQL_USER" \
-p"$MYSQL_PASS" \
--single-transaction \
--routines \
--triggers \
--events \
--source-data=2 \
--flush-logs \
--set-gtid-purged=AUTO \
--databases $DATABASES \
2>> "$LOG_FILE" \
| gzip > "$BACKUP_FILE"
check_result "数据库备份 → $BACKUP_FILE"
# ========== 备份验证 ==========
# 检查压缩文件是否可以正常解压
gzip -t "$BACKUP_FILE" 2>> "$LOG_FILE"
check_result "压缩文件完整性检查"
# 检查文件大小(小于1MB可能有问题)
FILE_SIZE=$(stat -f%z "$BACKUP_FILE" 2>/dev/null || stat -c%s "$BACKUP_FILE" 2>/dev/null)
if [ "$FILE_SIZE" -lt 1048576 ]; then
log_msg "WARNING: 备份文件异常小 (${FILE_SIZE} bytes),请人工检查"
fi
log_msg "备份文件大小: $(du -h "$BACKUP_FILE" | cut -f1)"
# ========== 清理过期备份 ==========
log_msg "清理 ${RETENTION_DAYS} 天前的备份..."
find "$BACKUP_DIR" -name "full_backup_*.sql.gz" -mtime +${RETENTION_DAYS} -delete
find "$BACKUP_DIR" -name "backup_*.log" -mtime +${RETENTION_DAYS} -delete
check_result "过期备份清理"
log_msg "备份任务完成"
配置定时任务
# 编辑crontab
crontab -e
# 每天凌晨3点执行全量备份
0 3 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup_cron.log 2>&1
# 每周日凌晨2点执行XtraBackup全量,其余天做增量
0 2 * * 0 /usr/local/bin/xtrabackup_full.sh
0 2 * * 1-6 /usr/local/bin/xtrabackup_incr.sh
备份用户权限
为备份专门创建一个权限最小化的数据库用户:
-- 创建备份专用用户
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'backup_pass_here';
-- mysqldump所需权限
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, PROCESS, RELOAD
ON *.* TO 'backup_user'@'localhost';
-- 如果需要--master-data,还需要REPLICATION CLIENT
GRANT REPLICATION CLIENT ON *.* TO 'backup_user'@'localhost';
-- XtraBackup所需权限
GRANT BACKUP_ADMIN, RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT
ON *.* TO 'backup_user'@'localhost';
FLUSH PRIVILEGES;
🤔 想一想 备份脚本中使用了明文密码。在生产环境中,你会用什么方式来避免密码泄露?提示:了解一下
mysql_config_editor和.mylogin.cnf。
七、生产环境备份方案选型——量体裁衣
不同规模的业务需要不同的备份方案。以下是几种典型场景的建议。
小型业务(数据量 < 10GB)
方案:mysqldump全量 + binlog
频率:每日凌晨全量备份
保留:本地15天 + 远程存储30天
工具链:
mysqldump → gzip压缩 → 本地存储
→ rsync/s3cmd上传到远程
mysqldump完全够用。10GB的数据库备份大约需要10-30分钟,对业务影响很小。
中型业务(10GB - 500GB)
方案:XtraBackup全量 + 增量 + binlog
频率:每周一次全量 + 每日增量
保留:本地7天 + 远程存储30天
工具链:
XtraBackup全量(周日) → 增量(周一到周六)
binlog持续归档到远程存储
每月恢复演练一次
大型业务(> 500GB)
方案:XtraBackup + 存储层快照 + binlog
频率:存储快照每日 + XtraBackup每周
特殊处理:
- 利用云存储的快照功能(如AWS EBS Snapshot)
- 从库上执行备份,不影响主库
- binlog实时归档到对象存储
- 备份完成后自动验证
备份方案核查清单
无论选择哪种方案,都要确保覆盖以下要点:
备份策略:
□ 全量备份频率和时间窗口
□ 增量备份频率
□ binlog归档策略
□ 备份保留周期
存储安全:
□ 备份文件是否加密
□ 是否有异地副本(不同机房/云区域)
□ 存储空间监控和告警
恢复能力:
□ 恢复流程文档化
□ 定期恢复演练
□ RTO(恢复时间目标)是否满足业务要求
□ RPO(恢复点目标)是否满足业务要求
监控告警:
□ 备份任务成功/失败告警
□ 备份文件大小异常告警
□ binlog磁盘空间告警
□ 备份过期清理确认
RPO与RTO
这两个指标决定了你的备份方案是否满足业务需求:
RPO RTO
←──────────→ ←─────────→
数据可能丢失 恢复所需
的最大时长 的最大时长
──┬──────────┬──────────────┬─────────┬──→ 时间
上次备份 故障发生 开始恢复 恢复完成
- RPO(Recovery Point Objective):最多能容忍丢多少数据。如果每天全量+实时binlog归档,RPO可以做到接近0
- RTO(Recovery Time Objective):从故障发生到业务恢复最长能等多久。这取决于备份大小和恢复方式——XtraBackup比mysqldump恢复更快
-- 查看当前binlog配置,评估RPO
SHOW VARIABLES LIKE 'sync_binlog';
-- sync_binlog=1 表示每次事务提交都刷盘,RPO最优
SHOW VARIABLES LIKE 'expire_logs_days';
-- 或 MySQL 8.0+
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
-- 确保binlog保留时间大于两次全量备份的间隔
📝 掌握度自测
- 逻辑备份和物理备份的核心区别是什么?各自适合什么规模的数据库?
- mysqldump的
--single-transaction参数为什么对InnoDB表至关重要?它的底层原理是什么? - XtraBackup增量备份的恢复步骤是什么?为什么中间步骤需要
--apply-log-only? - 描述一次完整的PITR流程:从发现误操作到数据恢复,需要哪些步骤?
- RPO和RTO分别代表什么?如何设计备份方案来满足”RPO < 1分钟,RTO < 30分钟”的要求?
💡 自我评估
- 答对5题:你已经具备设计和实施生产级备份方案的能力,记得定期做恢复演练
- 答对3-4题:核心概念清楚,建议在测试环境中完整走一遍”备份→模拟故障→恢复”的流程
- 答对0-2题:备份恢复是DBA最核心的技能之一,建议先从mysqldump开始动手实践,再逐步了解XtraBackup和PITR
购买课程解锁全部内容
让查询飞起来:MySQL 从索引到主从高可用
¥29.90