一、备份还原
1、 为什么要进行 MySQL 备份与还原?
在生产环境中,数据就是企业的生命线。备份与还原(Backup & Recovery)不仅仅是简单的复制文件,它是数据库管理员(DBA)最后的救命稻草。
1. 核心原因
-
防止数据丢失:硬件故障(硬盘损坏)、系统崩溃或自然灾害。
-
人为操作失误 :这是最常见的原因!比如程序员不小心执行了
DROP DATABASE或忘记加WHERE条件的DELETE语句。 -
安全防御:应对勒索病毒攻击或黑客恶意篡改数据。
-
版本回滚:在进行数据库架构升级或重大代码变更后,如果出现 Bug,需要快速回回到正常状态。
-
合规与审计:许多行业(如金融、医疗)法律规定必须定期保留数据备份。
2、 什么是备份(Backup)
备份是指通过特定的手段,将数据库的逻辑结构(建库、建表语句)和物理数据(记录行、索引文件)导出并另行保存的过程。
-
它的本质:是数据的"副本"。
-
衡量指标:
-
RPO (Recovery Point Objective):恢复点目标。即发生故障时,你最多能容忍丢失多少分钟的数据。
-
RTO (Recovery Time Objective):恢复时间目标。即发生故障后,你需要多久能把业务重新拉起来。
-
3、 什么是还原(Recovery / Restore)
还原是指当数据库发生故障或数据被破坏时,利用之前保存的备份副本,将数据库恢复到某个已知正常状态的操作。
-
还原的逻辑:
-
物理还原:把备份的文件直接拷贝回数据库目录。
-
逻辑还原:把备份的 SQL 语句在数据库里重新"跑一遍"。
-
日志追溯:利用二进制日志(Binlog)将全量备份之后产生的新数据"重做(Redo)"一遍,实现数据的无损恢复。
-
4、备份类型
1. 根据服务器状态分类(热、温、冷)
这是衡量备份对业务影响程度的关键指标。
2. 根据对象分类(物理 vs 逻辑)
这决定了备份的速度和恢复的灵活性。
-
物理备份 (Physical Backup):
-
定义 :直接复制数据库的物理文件(如 Data 目录下的
.ibd、索引文件、日志文件等)。 -
优点:恢复极快,适合大数据量(TB 级)。
-
缺点:文件通常比逻辑备份大,且跨平台能力差(如从 Linux 恢复到 Windows 可能会有问题)。
-
-
逻辑备份 (Logical Backup):
-
定义 :将数据库结构和数据导出为文本文件(通常是
.sql或.csv格式)。 -
优点:可读性强,可以精确到单表、甚至单行恢复,跨平台性极佳。
-
缺点:备份和恢复速度慢,不适合超大型数据库。
-
3. 根据数据收集量分类(全量、增量、差异)
这是制定"备份计划"的核心逻辑。
-
完全备份 (Full Backup):
- 备份数据库中全部的数据。它是后续所有备份的基础。
-
增量备份 (Incremental Backup):
-
逻辑 :仅备份自上一次备份(无论是全备还是增量)以来变化的数据。
-
优点:备份文件小,占用空间少,备份快。
-
缺点:恢复麻烦,需要按顺序合并所有增量包。
-
-
差异备份 (Differential Backup):
-
逻辑 :仅备份自上一次完全备份以来变化的数据。
-
优点:恢复比增量备份简单,只需要全备+最后一次差异备。
-
缺点:随着时间推移,差异备份的文件会越来越大。
-
5、常用备份工具
1. mysqldump(官方自带 · 逻辑备份)
这是每一位开发者都必须掌握的"国民级"工具。
-
类型:逻辑备份(导出 SQL 语句)。
-
优点:
-
无需安装:MySQL 客户端自带。
-
兼容性强 :生成的
.sql文件可以方便地在不同版本的 MySQL 甚至不同数据库间迁移。 -
灵活:支持备份单表、单库或全库。
-
-
缺点:
- 速度慢:大数据量(如超过 50G)时,导出和还原效率较低。
-
适用场景:小型数据库、开发环境测试、单表数据迁移。
2. mydumper(第三方增强 · 高性能逻辑备份)
如果 mysqldump 太慢,它是最佳的替代方案。
-
类型:逻辑备份。
-
优点:
-
多线程并发:备份和还原速度远快于 mysqldump。
-
文件切分:将表自动切分成多个小文件,方便管理。
-
一致性好:通过更精确的锁机制控制。
-
-
缺点:
- 额外安装:需要单独下载安装包(但在 RHEL 9 中安装很简便)。
-
适用场景:中型数据库、对备份时长有要求的生产环境。
3. Percona XtraBackup(物理备份专业户)
大厂、大数据的首选方案。
-
类型:物理备份(拷贝二进制数据文件)。
-
优点:
-
极速:几乎是磁盘 IO 的上限,适合 TB 级数据。
-
热备:备份时对业务几乎无影响(针对 InnoDB 引擎)。
-
-
缺点:
-
复杂度高:安装步骤多,对新手不友好。
-
不可跨平台:备份的文件通常只能还原到相同或兼容版本的 Linux 环境下。
-
-
适用场景:大型企业级生产环境、核心数据库的全量备份。
4. 小结
工具名称----->安装难度----->备份类型----->速度----->压缩/远程支持----->备份即时点
mysqldump----->系统自带----->逻辑----->慢----->支持----->不支持
mydumper----->简单----->逻辑----->快----->支持----->支持
xtrabackup----->较难----->物理----->较快----->不直接支持----->支持
二、实战
1、数据准备
1. 开启 Binlog (关键)
在 RHEL 9.7 中,MySQL 8.0 默认通常是开启的,但务必先确认:
sql
-- 登录 MySQL 后执行
show variables like 'log_bin';
检查点 :确保输出结果为 ON 。如果是 OFF,请修改 /etc/my.cnf 并重启服务。

2. 准备库和表
我们将创建一个 school 数据库,并建立学生表 student 和成绩表 score。
第一步:创建库和表
sql
-- 1. 创建数据库
CREATE DATABASE school DEFAULT CHARSET utf8mb4;
USE school;
-- 2. 创建学生表 student
CREATE TABLE student (
id INT(10) NOT NULL UNIQUE PRIMARY KEY,
name VARCHAR(20) NOT NULL,
sex VARCHAR(4),
birth YEAR,
department VARCHAR(20),
address VARCHAR(50)
);
-- 3. 创建成绩表 score
CREATE TABLE score (
id INT(10) NOT NULL UNIQUE PRIMARY KEY AUTO_INCREMENT,
stu_id INT(10) NOT NULL,
c_name VARCHAR(20),
grade INT(10)
);
第二步:插入首批业务数据(作为"全量"基础)
sql
-- 插入学生记录
INSERT INTO student VALUES(901,'张老大', '男',1985,'计算机系', '北京市海淀区');
INSERT INTO student VALUES(902,'张老二', '男',1986,'中文系', '北京市昌平区');
INSERT INTO student VALUES(903,'张三', '女',1990,'中文系', '湖南省永州市');
INSERT INTO student VALUES(904,'李四', '男',1990,'英语系', '辽宁省阜新市');
INSERT INTO student VALUES(905,'王五', '女',1991,'英语系', '福建省厦门市');
INSERT INTO student VALUES(906,'王六', '男',1988,'计算机系', '湖南省衡阳市');
-- 插入成绩记录
INSERT INTO score VALUES(NULL,901, '计算机',98);
INSERT INTO score VALUES(NULL,901, '英语', 80);
INSERT INTO score VALUES(NULL,902, '计算机',65);
INSERT INTO score VALUES(NULL,902, '中文',88);
INSERT INTO score VALUES(NULL,903, '中文',95);
INSERT INTO score VALUES(NULL,904, '计算机',70);
INSERT INTO score VALUES(NULL,904, '英语',92);
INSERT INTO score VALUES(NULL,905, '英语',94);
第三步:检查数据是否插入
sql
SELECT * FROM student;

2、使用 mysqldump 进行完全备份
1. 备份前的环境确认
在执行备份前,我们再次确认数据状态,确保 school 库中有我们刚刚插入的 6 条学生数据和 8 条成绩数据 。
2. 执行完全备份命令
打开终端(Terminal),创建一个备份目录并执行 mysqldump。根据文档建议,备份单个数据库时使用 -B 参数可以带上建库语句 。
powershell
# 1. 创建并进入备份目录
mkdir -p /mysqlbak
cd /mysqlbak
# 2. 执行备份
# --opt: 启用快速备份、优化表结构等多种选项
# -B: 指定数据库,备份文件中会包含 CREATE DATABASE 语句
mysqldump --opt -B school -u root -p > school.sql
输入密码后,/mysqlbak 目录下会生成 school.sql 文件。
3. 验证备份文件内容
为了确保备份成功,我们可以查看一下 SQL 文件的头部和关键部分:
powershell
# 查看备份文件的前几行
head -n 25 school.sql

4. 模拟数据增长(为增量备份做准备)
完全备份完成后,业务仍在运行。我们模拟向 student 表中再次插入 2 条新数据。
sql
USE school;
INSERT INTO student VALUES (907, 'xumubin', '男', 1995, '中文专业', '上海市'),
(908, 'wangzhao', '男', 2003, '导弹专业', '西安市');
注意:这两条数据(ID 907 和 908)并没有在刚才的 school.sql 文件中,它们只存在于当前的数据库和 Binlog(二进制日志) 中 。
5. 模拟灾难:误删数据库
现在,我们模拟最核心的实验环节------删库。
sql
-- 彻底删除数据库
DROP DATABASE school;
此时,如果你执行 show databases;,会发现 school 库已经消失了。

6. 立即刷新日志
删库后,为了保护现场,我们需要立即执行刷新日志的操作,产生一个新的 Binlog 文件,防止后续操作干扰恢复过程。
sql
FLUSH LOGS;
7. 解析 Binlog 与全量+增量恢复
1、恢复的第一步:还原完全备份
在处理增量数据之前,必须先把数据库恢复到"全备"时的状态。
powershell
# 方式 1:在 Linux 命令行执行
mysql -u root -p < /mysqlbak/school.sql
# 方式 2:进入 MySQL 命令行执行
# mysql> source /mysqlbak/school.sql
验证 :执行 SELECT * FROM school.student;,你会发现数据回来了,但只有 6 条 。最后插入的 xumubin 和 wangzhao 还没回来。

2、核心操作:定位并提取增量数据
我们需要从二进制日志(Binlog)中把那 2 条 INSERT 语句"抠"出来。
A. 找到正确的 Binlog 文件
在 MySQL 中执行:
sql
SHOW BINARY LOGS;
会看到多个文件(如 binlog.000001, binlog.000002)。通常发生误操作前的数据记录在刷新日志前 的那个文件里(假设是 binlog.000001)。

B. 查看并解析日志内容
由于 MySQL 8.0/5.7 的日志可能是加密或以 Base64 存储的,直接查看会乱码。
powershell
# 使用 mysqlbinlog 工具查看内容
# -vv 参数可以显示伪 SQL 语句,方便我们定位
mysqlbinlog --base64-output=DECODE-ROWS -vv /var/lib/mysql/binlog.000002

发现了导弹专业,说明,增量备份的时候,操作保存到了002日志中,接下来进行还原即可。
3、执行增量恢复
基于位置点恢复 (Start-Position)
如何精准定位 Binlog 位点?
-
搜寻起点 :通过
mysqlbinlog -vv找到全备后的第一条INSERT语句,记录其上方的# at [数字]。 -
搜寻终点 :通过
/DROP DATABASE搜索误操作语句,记录该语句起始 位置的# at [数字]。 -
执行提取 :
mysqlbinlog --start-position=起点 --stop-position=终点 binlog.xxxxx > recovery.sql。

通过 mysqlbinlog 工具查看 binlog.000002,寻找丢失数据的坐标。
-
Start Position : 5717 (对应
INSERT事务的BEGIN) -
Stop Position : 5908 (对应
DROP DATABASE的起点)
避坑指南 :在 MySQL 8.0 中,如果直接提取,还原时会因为 GTID 幂等性 导致数据无法写入(系统认为这些事务已经跑过了)。
powershell
# 提取指定位置段的日志
# 必须添加 --skip-gtids 参数
mysqlbinlog --start-position=5717 --stop-position=5908 \
--skip-gtids \
/var/lib/mysql/binlog.000002 > /mysqlbak/increment.sql
sql
# 执行还原
-- 进入 MySQL 命令行执行
USE school;
SET sql_log_bin=0; -- 临时关闭 binlog 记录,提高效率
SOURCE /mysqlbak/increment.sql;
SET sql_log_bin=1;
或者
-- 也可以直接不登录,直接使用命令执行
mysql -uroot -p school < /mysqlbak/increment.sql
4、进入数据库检查:
powershell
SELECT * FROM school.student;

3、 mydumper备份
1. 为什么选择 mydumper?
在笔记中,首先要写出它的优势,这能体现你的选型思考:
-
多线程并发 :备份和还原速度远超
mysqldump。 -
文件切分 :它会将表自动拆分成多个小文件(每个表一个
.sql,甚至每个 chunk 一个文件),方便管理和部分恢复。 -
一致性控制 :通过
FTWRL(Flush Tables With Read Lock)更好地控制备份瞬间的一致性。
2. 安装 mydumper
sql
wget https://github.com/mydumper/mydumper/releases/download/v0.21.3-1/mydumper-0.21.3-1.el9.x86_64.rpm
sql
dnf install mydumper-0.21.3-1.el9.x86_64.rpm -y
验证是否安装成功
powershell
[root@localhost ~]# mydumper --version
mydumper v0.21.3-1, built against MySQL 8.4.8 with SSL support
3、实战演练:多线程备份 school 库
powershell
# 创建备份存放目录
mkdir -p /mysqlbak/mydumper_out
# 执行多线程备份
# -u 用户, -p 密码, -B 数据库, -t 线程数(建议设为 CPU 核心数), -o 输出目录
mydumper -u root -p 123456 -B school -t 4 -o /mysqlbak/mydumper_out/
4、观察备份结果
执行完后,进入 /mysqlbak/mydumper_out/ 目录
powershell
cd /mysqlbak/mydumper_out/
powershell
ll

文件说明:
-
metadata:记录了备份开始和结束的时间,以及最重要的 Binlog 位点(Pos)。 -
school-schema-create.sql:建库语句。 -
school.student-schema.sql:student表结构。 -
school.student.00000.sql:student表的数据内容。 -
school.score-schema.sql:score表结构。
5、模拟损坏与 myloader 还原
mydumper 对应的还原工具是 myloader。
再次删库 :DROP DATABASE school;
执行还原:
powershell
# -u 用户, -p 密码, -B 目标库, -d 备份文件目录
myloader -u root -p 123456 -B school -d /mysqlbak/mydumper_out/

验证是否还原
powershell
mysql -uroot -p'123456'
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| school |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> select * from school.student;
+-----+-----------+------+-------+--------------+--------------------+
| id | name | sex | birth | department | address |
+-----+-----------+------+-------+--------------+--------------------+
| 901 | 张老大 | 男 | 1985 | 计算机系 | 北京市海淀区 |
| 902 | 张老二 | 男 | 1986 | 中文系 | 北京市昌平区 |
| 903 | 张三 | 女 | 1990 | 中文系 | 湖南省永州市 |
| 904 | 李四 | 男 | 1990 | 英语系 | 辽宁省阜新市 |
| 905 | 王五 | 女 | 1991 | 英语系 | 福建省厦门市 |
| 906 | 王六 | 男 | 1988 | 计算机系 | 湖南省衡阳市 |
| 907 | xumubin | 男 | 1995 | 中文专业 | 上海市 |
| 908 | wangzhao | 男 | 2003 | 导弹专业 | 西安市 |
+-----+-----------+------+-------+--------------+--------------------+
8 rows in set (0.00 sec)

4、LVM 快照实现"瞬时"物理备份
1. LVM 快照备份的原理
它就像给数据库拍了一张'全身照'。拍照的一瞬间,数据库会短暂锁表,快照创建完(通常只需几秒)立刻解锁。随后你可以慢慢拷贝这张照片里的数据,而数据库已经恢复了正常的读写。
2. 实战环境检查
首先,我们要确认你的分区支持 LVM:
powershell
# 查看逻辑卷信息
lvs
# 查看卷组剩余空间(快照需要占用卷组剩余空间)
vgs
注意:如果你的 MySQL 没装在 LVM 分区上,这个实验需要在虚拟机里添加一块新硬盘并配置 LVM 挂载点

这种是不行的,需要重新添加一块磁盘。
添加并配置新磁盘(模拟生产环境)
-
在虚拟机设置里添加一块新硬盘(比如 20G)。
-
创建物理卷 (PV) :
pvcreate /dev/sdb -
创建卷组 (VG) :
vgcreate vg_mysql /dev/sdb -
创建逻辑卷 (LV) :
lvcreate -L 10G -n lv_data vg_mysql -
格式化并挂载 : 格式化为
xfs或ext4,然后把 MySQL 的数据迁移到这个新分区上。
注意:在实际生产中,MySQL 数据必须存放在独立的 LVM 逻辑卷上。如果数据在根分区且没有剩余卷组空间,是无法创建快照的。

第一步:构建 LVM 逻辑卷 (物理机/虚拟机操作)
我们要把这块新硬盘变成 LVM 结构。
先使用lbslk查看新加的磁盘的名称
powershell
[root@localhost ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 20G 0 disk
sr0 11:0 1 12.7G 0 rom /mnt
nvme0n1 259:0 0 100G 0 disk
├─nvme0n1p1 259:1 0 600M 0 part /boot/efi
├─nvme0n1p2 259:2 0 1G 0 part /boot
└─nvme0n1p3 259:3 0 98.4G 0 part
├─rhel-root 253:0 0 63.5G 0 lvm /
├─rhel-swap 253:1 0 3.9G 0 lvm
└─rhel-home 253:2 0 31G 0 lvm /home
检查点 :看 sda 是否存在,且它的 TYPE 应该是 disk,下方不应该有任何 part(分区)。
powershell
# 1. 创建物理卷 (PV)
pvcreate /dev/sda
# 2. 创建卷组 (VG),命名为 vg_mysql
vgcreate vg_mysql /dev/sda
# 3. 创建逻辑卷 (LV),分配 10G 空间(留 10G 给快照用)
lvcreate -L 10G -n lv_mysql_data vg_mysql
# 4. 格式化为ext4文件系统
mkfs.ext4 /dev/vg_mysql/lv_mysql_data

第二步:数据大迁移(将 MySQL 搬入 LVM)
这是模拟生产环境中最真实的一步:如何不停机/短暂停机迁移数据。
powershell
# 1. 停止 MySQL 服务(确保数据一致性)
systemctl stop mysqld
# 2.备份所有的数据文件到指定的地方
tar -zcf /tmp/mysql.tar.gz /var/lib/mysql/*
# 3.查看是否打包
tar -tf /tmp/mysql.tar.gz
# 4.挂载逻辑卷到当前 MySQL 的数据目录里
mount /dev/vg_mysql/lv_mysql_data /var/lib/mysql
# 5.查看是否还有文件,这是查看是查看不到的,系统已经把文件隐藏了
ls /var/lib/mysql
# 6.解压/tmp/mysql.tar.gz到/
tar -xzf /tmp/mysql.tar.gz -C /
# 7.查看/var/lib/mysql下面是否有数据
ls /var/lib/mysql
现在发现有数据了
powershell
[root@localhost ~]# ls /var/lib/mysql
auto.cnf binlog.000003 binlog.index client-cert.pem '#ib_16384_1.dblwr' '#innodb_redo' mysql performance_schema school sys
binlog.000001 binlog.000004 ca-key.pem client-key.pem ib_buffer_pool '#innodb_temp' mysql.ibd private_key.pem server-cert.pem undo_001
binlog.000002 binlog.000005 ca.pem '#ib_16384_0.dblwr' ibdata1 lost+found mysql_upgrade_history public_key.pem server-key.pem undo_002
[root@localhost ~]#
更改目录的权限
powershell
chown -R mysql: /var/lib/mysql
启动MySQL
powershell
systemctl start mysqld
3. LVM备份
安装rsync工具
powershell
dnf install rsync -y
编写备份脚本vim bak_mysql.sh
powershell
#!/bin/bash
# 1. 定义备份目录(确保 /bak 目录存在)
bak_dir=/bak/$(date +%F)
[ -d ${bak_dir} ] || mkdir -p ${bak_dir}
# 2. 执行快照(核心路径修改点)
# 注意:这里 /dev/vg_mysql/lv_mysql_data 是你刚刚创建的源卷
# -n lv_mysql_s 是快照的名字
echo "flush tables with read lock; system lvcreate -n lv_mysql_s -L 500M -s /dev/vg_mysql/lv_mysql_data; unlock tables;" | mysql -uroot -p'123456'
# 3. 挂载快照卷
[ -d /mnt/mysql/ ] || mkdir -p /media/mysql
# 挂载路径也要对应卷组名和快照名
mount /dev/vg_mysql/lv_mysql_s /media/mysql
# 4. 同步数据
rsync -az /media/mysql/ ${bak_dir}
# 5. 清理现场
if [ $? -eq 0 ]; then
umount /media/mysql/ && lvremove -f /dev/vg_mysql/lv_mysql_s &>/dev/null
echo "恭喜!LVM 物理备份已成功完成,存放于 ${bak_dir}"
fi
注意:这里是挂载在/media/mysql/,不能使用/mnt/mysql,因为使用这个目录的话,系统会报错,因为/mnt目录只有只读权限,不能在/mnt下面创建目录,因此这里使用的挂载目录是/media
检查脚本是否有语法错误
powershell
sh -n bak_mysql.sh
没有语法错误就直接运行脚本
powershell
bash bak_mysql.sh

备份完成后就可以查看备份目录中是否有文件。
powershell
ls /bak/2026-04-04/
# /bak/2026-04-04/ 这个日期是你自己执行lvs备份数据库的这个日期,也就是你执行这个命令当天的日期
4. 还原数据
powershell
# 1. 停止当前运行的数据库服务(防止文件冲突)
systemctl stop mysqld
# 2. 修改 MySQL 核心配置文件
vim /etc/my.cnf
# 找到 [mysqld] 模块下的 datadir 参数
# 将 datadir=/var/lib/mysql 修改为你的备份目录路径
# 例如:datadir=/bak/2026-04-04/
# 3. 关键步骤:修正目录权限
# 物理备份的文件必须属于 mysql 用户,否则数据库无法启动
chown -R mysql:mysql /bak/2026-04-04
# 4. 启动数据库
systemctl start mysqld
# 5. 登录验证
mysql -uroot -p'你的密码'
# 此时执行 show databases; 你会发现数据已经完全恢复到了备份时的状态

至此,LVM备份成功。
5、xtrabackup备份
1. 工具简介 (Why XtraBackup?)
XtraBackup 是由 Percona 公司开发的开源物理备份工具。
-
备份类型:物理备份(直接复制数据文件)。
-
备份速度:较快 。
-
核心优势 :支持热备份,即在备份过程中数据库的读、写操作均不受影响。
2. 环境准备与安装 (针对 RHEL 9.x)
对于 MySQL 8.0 及以上版本,建议使用 XtraBackup 8.0/8.4 系列。
powershell
# 1. 下载并安装 Percona 仓库及工具
dnf install https://repo.percona.com/yum/percona-release-latest.noarch.rpm -y
percona-release enable-only tools release
dnf install percona-xtrabackup-80 -y
配置备份专属用户(推荐):
sql
CREATE USER 'bkuser'@'%' IDENTIFIED with caching_sha2_password BY 'Back@123';
GRANT BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'bkuser'@'%';
GRANT SELECT ON performance_schema.log_status TO 'bkuser'@'%';
GRANT SELECT ON performance_schema.keyring_component_status TO 'bkuser'@'%';
GRANT SELECT ON performance_schema.replication_group_members TO 'bkuser'@'%';
FLUSH PRIVILEGES;
3. 全量备份 (Full Backup) 实战
执行物理备份时,数据将直接拷贝到指定目录 。
powershell
# 创建备份存放目录(建议存放在您的新磁盘挂载点下)
mkdir -p /backup/xtra/{base,incr1,incr2}
# 执行全量备份
xtrabackup --defaults-file=/etc/my.cnf --backup \
--target-dir=/backup/xtra/base \
--user=bkuser --password='Back@123'

4. 增量备份 (Incremental Backup) 实战
增量备份仅备份自上次备份以来变化的数据。插入数据
sql
INSERT INTO school.student (id, name, sex, birth, department, address)
VALUES (8, 'alice', '女', 2001, '计算机科学', '北京市海淀区');
第一次增量备份(基于全备):
powershell
xtrabackup --defaults-file=/etc/my.cnf --backup --target-dir=/backup/xtra/incr1/ --incremental-basedir=/backup/xtra/base --user=bkuser --password='Back@123'
--defaults-file=/etc/my.cnf
含义:指定 MySQL 的配置文件路径。
作用:让 xtrabackup 读取该文件中的配置(如数据目录位置等),确保备份环境与数据库运行环境一致。
--backup
含义:启动备份操作。
作用:这是告诉 xtrabackup 开始执行备份任务的核心参数。
--target-dir=/backup/xtra/incr1/
含义:指定备份的目的目录。
作用:备份生成的文件将存储在这个路径下。在这个例子中,它是一个增量备份的存储目录。
--incremental-basedir=/backup/xtra/base
含义:增量备份的基础目录。
作用:指定上一次全量备份(或上一次增量备份)所在的目录。xtrabackup 会对比这个目录中的数据,只备份发生变化的数据,从而实现增量备份。
--user=bkuser
含义:指定连接数据库的用户名。
作用:这里使用的是之前创建的 bkuser 用户。
--password='Back@123'
含义:指定连接数据库的密码。
作用:这是 bkuser 用户的密码,用于验证身份。

再次插入一条数据:
powershell
INSERT INTO school.student (id, name, sex, department)
VALUES (9, 'bob', '男', '软件工程');
第二次增量备份(基于上一次增量):
powershell
/usr/bin/xtrabackup --defaults-file=/etc/my.cnf --backup --target-dir=/backup/xtra/incr2/ --incremental-basedir=/backup/xtra/incr1/ --user=bkuser --password='Back@123'

5. 数据还原
powershell
-- 模拟破坏数据 (注意:此操作会删除数据库,请在测试环境执行)
mysql> drop database school;
-- 准备完全备份 (注意:必须加上 --apply-log-only,除了最后一次)
xtrabackup --prepare --apply-log-only --target-dir=/backup/xtra/base/
-- 应用第一次增量备份到完全备份 (注意:--incremental-dir 指向 incr1)
xtrabackup --prepare --apply-log-only --target-dir=/backup/xtra/base/ --incremental-dir=/backup/xtra/incr1/
-- 应用第二次增量备份到完全备份 (注意:--incremental-dir 指向 incr2)
xtrabackup --prepare --apply-log-only --target-dir=/backup/xtra/base/ --incremental-dir=/backup/xtra/incr2/
-- 最后执行恢复操作
# 1. 最终 Prepare (注意:最后一次合并不需要 --apply-log-only,确保数据回滚段清理)
xtrabackup --prepare --target-dir=/backup/xtra/base/
# 2. 停止 MySQL 服务
systemctl stop mysqld
# 3. 清空原数据目录 (注意:此操作极其危险,会删除所有现有数据,请确保已备份)
rm -rf /var/lib/mysql/*
# 4. 将备份文件拷贝回原数据目录
xtrabackup --copy-back --target-dir=/backup/xtra/base/
# 5. 修改文件属主 (注意:必须改回 mysql 用户,否则数据库无法启动)
chown -R mysql:mysql /var/lib/mysql
# 6. 启动 MySQL 服务
systemctl start mysqld
3、基于GTID的备份实战
一、 什么是 GTID?
GTID (Global Transaction Identifier) 即 全局事务标识符。
在传统的备份还原中,我们依赖 binlog 文件名和 position(位置点)来定位。但这种方式在多库环境或发生故障切换时非常混乱。 GTID 的核心意义 :为每一个提交的事务分配一个全网唯一的编号。无论事务被传送到哪台服务器,它的编号永远不变。
1. GTID 的格式
GTID 由两部分组成:
GTID = source_id : transaction_id
-
source_id :源服务器的唯一标识(即
server_uuid)。 -
transaction_id:在该服务器上提交的事务序列号,从 1 开始顺序排列。
-
示例 :
6181523d-bc2e-11ea-a78b-000c29221146:1-10(代表该 UUID 服务器上的前 10 个事务)。
2. 存储与版本支持
-
版本支持 :MySQL 5.6 引入,5.7 完善,8.0 成为标配。
-
存储位置:
-
内存 :实时记录在全局变量
@@global.gtid_executed中。 -
磁盘 :持久化在
binlog日志以及系统表mysql.gtid_executed中。
-
二、 如何开启 GTID
在 RHEL 9.7 (MySQL 8.4.0) 中,开启 GTID 需要修改配置文件。
实战步骤:
- 编辑配置文件:
plain
vim /etc/my.cnf
- 在
[mysqld]模块下添加以下四行:
powershell
gtid_mode=ON # 开启 GTID 模式
enforce-gtid-consistency=ON # 强制 GTID 一致性(如不支持 create table ... select)
log-bin=mysql-bin # 必须开启 Binlog
log-slave-updates=ON # 从库也记录事务日志(高可用必备)
- 重启服务:
plain
systemctl restart mysqld
- 验证状态:
sql
mysql> show variables like '%gtid%';
-- 看到 gtid_mode 为 ON 即表示成功!
三、 DDL 和 DML 如何查看 GTID?
开启 GTID 后,每一个操作都会带上身份勋章。
1. 查看当前已经执行了哪些 GTID
sql
show binary log status;
2. 查看具体 SQL 对应的 GTID
使用 mysqlbinlog 工具查看日志:
powershell
# 查看最新的 binlog 文件
mysqlbinlog -vv /var/lib/mysql/mysql-bin.000001
-
DDL (如 Create) :你会看到在
CREATE TABLE语句之前,有一行SET @@SESSION.GTID_NEXT='...:N'。 -
DML (如 Insert) :在事务开始的
BEGIN之前,同样会打上 GTID 标记。
四、 保姆级实战:如何利用 GTID 实现备份恢复?
这是最关键的实战环节。假设我们不小心删掉了 school 库中的几条数据,需要通过 Binlog 恢复到指定状态。
场景模拟:
-
正常操作:插入了 5 条数据(产生 GTID 1-5)。
-
误操作:删除了数据(产生 GTID 6)。
-
需求:恢复到 GTID 1-5 的状态。
先创建数据
sql
-- 1. 创建名为 gtid 的实验数据库
mysql> create database gtid;
Query OK, 1 row affected (0.01 sec)
-- 2. 切换到该数据库
mysql> use gtid;
Database changed
-- 3. 创建一张简单的实验表 t1,包含一个整数列 id
-- 此时会产生一个 DDL 事务,分配一个全新的 GTID
mysql> create table t1(id int);
Query OK, 0 rows affected (0.00 sec)
-- 4. 插入数据(语法错误示例)
-- 虽然执行失败,但由于语法解析阶段就报错了,通常不会消耗 GTID 序列号
mysql> insert into t1(12);
ERROR 1064 (42000): ...(语法错误)
-- 5. 正确插入第一条数据:12
-- 产生一个 DML 事务,GTID 序列号增加
mysql> insert into t1 value(12);
Query OK, 1 row affected (0.00 sec)
-- 6. 正确插入第二条数据:13
-- 产生一个 DML 事务,GTID 序列号继续增加
mysql> insert into t1 value(13);
Query OK, 1 row affected (0.00 sec)
-- 7. 正确插入第三条数据:23
-- 产生一个 DML 事务,GTID 序列号继续增加
mysql> insert into t1 value(23);
Query OK, 1 row affected (0.00 sec)
-- 8. 查看当前的二进制日志状态(核心观察点)
mysql> show binary log status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000001 | 2560 | | | 74587089-3025-11f1-863c-000c2905496c:1-10 |
+------------------+----------+--------------+------------------+-------------------------------------------+
/* 【结果分析】:
- File: 当前正在写入的日志文件是 mysql-bin.000001。
- Position: 文件偏移量为 2560。
- Executed_Gtid_Set: 74587089-3025-11f1-863c-000c2905496c:1-10
表示该 UUID 对应的服务器上已经执行了从 1 到 10 号的所有事务。
这就意味着你刚才的建库、建表、插入等操作已经占用了这 10 个号中的一部分。
*/
-- 然后删除这个数据库,后续进行备份
drop database gtid;
步骤 1:寻找目标 GTID 范围
登录数据库查看:
powershell
mysql> show binlog events in 'mysql-bin.000001';
+------------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| mysql-bin.000001 | 4 | Format_desc | 1 | 127 | Server ver: 8.4.8, Binlog ver: 4 |
| mysql-bin.000001 | 127 | Previous_gtids | 1 | 158 | |
| mysql-bin.000001 | 158 | Gtid | 1 | 235 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:1' |
| mysql-bin.000001 | 235 | Query | 1 | 350 | use `school`; create table y1(id int) /* xid=16 */ |
| mysql-bin.000001 | 350 | Gtid | 1 | 429 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:2' |
| mysql-bin.000001 | 429 | Query | 1 | 506 | BEGIN |
| mysql-bin.000001 | 506 | Table_map | 1 | 556 | table_id: 94 (school.y1) |
| mysql-bin.000001 | 556 | Write_rows | 1 | 596 | table_id: 94 flags: STMT_END_F |
| mysql-bin.000001 | 596 | Xid | 1 | 627 | COMMIT /* xid=17 */ |
| mysql-bin.000001 | 627 | Gtid | 1 | 706 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:3' |
| mysql-bin.000001 | 706 | Query | 1 | 783 | BEGIN |
| mysql-bin.000001 | 783 | Table_map | 1 | 833 | table_id: 94 (school.y1) |
| mysql-bin.000001 | 833 | Write_rows | 1 | 873 | table_id: 94 flags: STMT_END_F |
| mysql-bin.000001 | 873 | Xid | 1 | 904 | COMMIT /* xid=24 */ |
| mysql-bin.000001 | 904 | Gtid | 1 | 983 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:4' |
| mysql-bin.000001 | 983 | Query | 1 | 1060 | BEGIN |
| mysql-bin.000001 | 1060 | Table_map | 1 | 1110 | table_id: 94 (school.y1) |
| mysql-bin.000001 | 1110 | Write_rows | 1 | 1150 | table_id: 94 flags: STMT_END_F |
| mysql-bin.000001 | 1150 | Xid | 1 | 1181 | COMMIT /* xid=25 */ |
| mysql-bin.000001 | 1181 | Gtid | 1 | 1258 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:5' |
| mysql-bin.000001 | 1258 | Query | 1 | 1368 | drop database school /* xid=28 */ |
| mysql-bin.000001 | 1368 | Gtid | 1 | 1445 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:6' |
| mysql-bin.000001 | 1445 | Query | 1 | 1553 | create database gtid /* xid=31 */ |
| mysql-bin.000001 | 1553 | Gtid | 1 | 1630 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:7' |
| mysql-bin.000001 | 1630 | Query | 1 | 1741 | use `gtid`; create table t1(id int) /* xid=36 */ |
| mysql-bin.000001 | 1741 | Gtid | 1 | 1820 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:8' |
| mysql-bin.000001 | 1820 | Query | 1 | 1895 | BEGIN |
| mysql-bin.000001 | 1895 | Table_map | 1 | 1943 | table_id: 98 (gtid.t1) |
| mysql-bin.000001 | 1943 | Write_rows | 1 | 1983 | table_id: 98 flags: STMT_END_F |
| mysql-bin.000001 | 1983 | Xid | 1 | 2014 | COMMIT /* xid=38 */ |
| mysql-bin.000001 | 2014 | Gtid | 1 | 2093 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:9' |
| mysql-bin.000001 | 2093 | Query | 1 | 2168 | BEGIN |
| mysql-bin.000001 | 2168 | Table_map | 1 | 2216 | table_id: 98 (gtid.t1) |
| mysql-bin.000001 | 2216 | Write_rows | 1 | 2256 | table_id: 98 flags: STMT_END_F |
| mysql-bin.000001 | 2256 | Xid | 1 | 2287 | COMMIT /* xid=39 */ |
| mysql-bin.000001 | 2287 | Gtid | 1 | 2366 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:10' |
| mysql-bin.000001 | 2366 | Query | 1 | 2441 | BEGIN |
| mysql-bin.000001 | 2441 | Table_map | 1 | 2489 | table_id: 98 (gtid.t1) |
| mysql-bin.000001 | 2489 | Write_rows | 1 | 2529 | table_id: 98 flags: STMT_END_F |
| mysql-bin.000001 | 2529 | Xid | 1 | 2560 | COMMIT /* xid=40 */ |
| mysql-bin.000001 | 2560 | Gtid | 1 | 2637 | SET @@SESSION.GTID_NEXT= '74587089-3025-11f1-863c-000c2905496c:11' |
| mysql-bin.000001 | 2637 | Query | 1 | 2741 | drop database gtid /* xid=42 */ |
+------------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+


由于都是在同一个日志文件中操作的,因此列出该日志文件中的开始的事务id和结束的事务id
powershell
mysql-bin.000001 74587089-3025-11f1-863c-000c2905496c:6
mysql-bin.000001 74587089-3025-11f1-863c-000c2905496c:11
# 但是呢这里的事务id=11不能取,因为这是删除数据库的事务id,因此结束id要取的是10
完整的事务id起始和结束如下:
powershell
mysql-bin.000001 74587089-3025-11f1-863c-000c2905496c:6
mysql-bin.000001 74587089-3025-11f1-863c-000c2905496c:10
步骤 2:利用 mysqlbinlog 导出数据(关键点!)
注意: 必须使用 --skip-gtids 参数,否则恢复时会因为幂等性被系统忽略。
powershell
# 截取 GTID 6 到 10 的数据导出到 SQL 文件
# 注意注意:一定要进入到/var/lib/mysql目录文件中去执行下面的命令
[root@localhost ~]# cd /var/lib/mysql
[root@localhost mysql]# mysqlbinlog --skip-gtids --include-gtids='74587089-3025-11f1-863c-000c2905496c:6-10' mysql-bin.000001 > /root/gtid1.sql
[root@localhost mysql]#
# 或者
mysqlbinlog --skip-gtids --include-gtids='89-3025-11f1-863c-000c2905496c:6-10' /var/lib/mysql/mysql-bin.000001 > /root/gtid1.sql
步骤 3:导入数据并控制 Binlog 生成
为了保证恢复过程不产生垃圾日志,也为了避免冲突:
sql
-- 1. 进入 MySQL
mysql -uroot -p
-- 2. 临时关闭当前会话的 Binlog 记录
set sql_log_bin=0;
-- 3. 导入 SQL
source /root/gtid1.sql;
-- 4. 重新开启 Binlog 记录
set sql_log_bin=1;

五、 GTID 模式下的备份恢复总结
-
优势:不再需要翻找 Position 字节偏移量,直接通过事务编号(1-5, 7-10)截取。
-
核心坑点 :幂等性。
-
如果你不加
--skip-gtids:MySQL 导入时会检查:"咦,GTID 1-5 我已经运行过了呀,为了数据安全,我跳过吧。" ------ 结果就是恢复了个寂寞。 -
如果你加了
--skip-gtids:MySQL 导入时会认为:"哦,这是一些没有身份的新数据,我得赶紧存进去。" ------ 恢复成功。
-
小结:每次操作数据库(增删改数据库/表/数据)的时候都会产生一个全局事务id。