日志体系与备份恢复
一、Binlog(二进制日志)
1.1 Binlog 的作用
Binlog 是 MySQL Server 层的日志,记录所有数据变更操作(DDL 和 DML),用于:
- 主从复制:从库读取主库 Binlog 同步数据
- 数据恢复:通过 Binlog 进行时间点恢复(Point-in-Time Recovery)
- 审计:记录数据变更历史
1.2 Binlog 的三种格式
| 格式 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| STATEMENT | 记录原始 SQL | 日志量小 | 某些函数(NOW()、UUID())在主从可能结果不同 |
| ROW(默认) | 记录每一行的变更 | 最精确,主从一致 | 日志量大 |
| MIXED | 自动选择 | 兼顾两者 | 复杂场景自动切换为 ROW |
sql
-- 查看当前 binlog 格式
SHOW VARIABLES LIKE 'binlog_format';
-- 设置 binlog 格式
SET GLOBAL binlog_format = 'ROW';
-- my.cnf 配置
[mysqld]
binlog_format = ROW
server_id = 1 -- 必须设置(主从复制需要)
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 7 -- 日志保留 7 天
max_binlog_size = 500M -- 每个 binlog 文件最大 500MB
推荐使用 ROW 格式:虽然日志量大,但数据一致性最好,主从复制不会"跑偏"。
1.3 Binlog 的常用操作
sql
-- 查看 binlog 文件列表
SHOW BINARY LOGS;
-- 查看当前正在写入的 binlog
SHOW MASTER STATUS;
-- 查看 binlog 事件
SHOW BINLOG EVENTS IN 'mysql-bin.000001' LIMIT 10;
-- 通过 mysqlbinlog 工具查看 binlog 内容
-- 命令行:
mysqlbinlog --base64-output=decode-rows -vv /var/log/mysql/mysql-bin.000001
-- 刷新 binlog(生成新文件)
FLUSH LOGS;
-- 删除指定 binlog 之前的日志
PURGE BINARY LOGS TO 'mysql-bin.000010';
-- 删除所有 binlog 之前指定日期的
PURGE BINARY LOGS BEFORE '2024-06-01 00:00:00';
-- 设置 binlog 过期时间(推荐自动清理)
SET GLOBAL expire_logs_days = 7;
1.4 通过 Binlog 恢复数据
sql
-- 场景:误删了某张表的数据,需要通过 binlog 恢复
-- 步骤1:找到误操作的时间点和 binlog 位置
-- 查看 binlog 事件
SHOW BINLOG EVENTS IN 'mysql-bin.000005';
-- 假设找到了误删的 binlog 位置:从 position 5000 开始
-- 步骤2:将 binlog 导出为 SQL(排除误删的语句)
-- 导出到误删之前的 SQL
mysqlbinlog --start-position=1 --stop-position=5000 \
/var/log/mysql/mysql-bin.000005 > recovery_before.sql
-- 步骤3:执行恢复
mysql -u root -p < recovery_before.sql
-- 更精确的做法:使用 mysqlbinlog 的时间点
mysqlbinlog --stop-datetime="2024-06-15 14:29:59" \
/var/log/mysql/mysql-bin.000005 | mysql -u root -p
-- 跳过误删的语句然后继续后面的操作
mysqlbinlog --start-position=6000 \
/var/log/mysql/mysql-bin.000005 | mysql -u root -p
1.5 Binlog 与 Redo Log 的区别
| 对比 | Binlog | Redo Log |
|---|---|---|
| 所属层级 | Server 层 | InnoDB 引擎层 |
| 记录内容 | 逻辑 SQL/行变更 | 物理页修改 |
| 记录方式 | 追加写 | 循环写 |
| 用途 | 主从复制、数据恢复 | 崩溃恢复 |
| 清理方式 | expire_logs_days | 循环覆盖 |
| 两阶段提交 | 参与(保证一致性) | 参与 |
二、备份方案
2.1 物理备份 vs 逻辑备份
| 对比 | 物理备份 | 逻辑备份 |
|---|---|---|
| 工具 | xtrabackup(Percona) | mysqldump |
| 备份内容 | 数据文件 | SQL 语句 |
| 备份速度 | 快 | 慢(大表尤其明显) |
| 恢复速度 | 快 | 慢 |
| 粒度 | 整个实例/库 | 表级别/行级别 |
| 适用场景 | 大数据量、全量备份 | 小数据量、迁移数据 |
2.2 mysqldump 逻辑备份
sql
-- 备份整个数据库
mysqldump -u root -p --databases mydb > mydb_backup.sql
-- 备份单表
mysqldump -u root -p mydb orders > orders_backup.sql
-- 备份多表
mysqldump -u root -p mydb orders users > mydb_part.sql
-- 常用参数
-- --single-transaction:InnoDB 一致性备份(不锁表)
-- --routines:导出存储过程和函数
-- --triggers:导出触发器
-- --master-data=2:记录 binlog 位置(注释形式)
mysqldump -u root -p \
--single-transaction \
--routines \
--triggers \
--master-data=2 \
mydb > full_backup.sql
-- 恢复
mysql -u root -p mydb < full_backup.sql
-- 压缩备份
mysqldump -u root -p --single-transaction mydb | gzip > mydb_backup.sql.gz
gunzip < mydb_backup.sql.gz | mysql -u root -p mydb
2.3 Xtrabackup 物理备份
sql
-- Percona XtraBackup 安装后使用
-- 全量备份
xtrabackup --backup \
--target-dir=/backup/mysql/full_20240601 \
--user=root \
--password=your_password
-- 准备备份(应用 redo log,使数据一致)
xtrabackup --prepare \
--target-dir=/backup/mysql/full_20240601
-- 恢复备份
xtrabackup --copy-back \
--target-dir=/backup/mysql/full_20240601 \
--datadir=/var/lib/mysql
2.4 增量备份(Xtrabackup)
sql
-- 周一:全量备份
xtrabackup --backup --target-dir=/backup/mysql/full
-- 周二到周日:增量备份(基于前一天的增量)
xtrabackup --backup \
--target-dir=/backup/mysql/incr_mon \
--incremental-basedir=/backup/mysql/full
xtrabackup --backup \
--target-dir=/backup/mysql/incr_tue \
--incremental-basedir=/backup/mysql/incr_mon
-- 恢复时:准备全量 + 应用增量
xtrabackup --prepare --apply-log-only --target-dir=/backup/mysql/full
xtrabackup --prepare --apply-log-only \
--target-dir=/backup/mysql/full \
--incremental-dir=/backup/mysql/incr_mon
xtrabackup --prepare \
--target-dir=/backup/mysql/full \
--incremental-dir=/backup/mysql/incr_tue
-- copy-back 恢复
xtrabackup --copy-back --target-dir=/backup/mysql/full
2.5 定时备份脚本
bash
#!/bin/bash
# 全量备份脚本
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d)
USER="root"
PASSWORD="your_password"
# 创建备份目录
mkdir -p $BACKUP_DIR/full_$DATE
# 全量备份
mysqldump -u$USER -p$PASSWORD \
--single-transaction \
--all-databases \
--master-data=2 \
--routines \
--triggers \
| gzip > $BACKUP_DIR/full_$DATE/full_backup.sql.gz
# 删除 30 天前的备份
find $BACKUP_DIR -name "full_*" -mtime +30 -exec rm -rf {} \;
echo "Backup completed: $DATE"
bash
# crontab 定时任务
# 每天凌晨 2 点执行全量备份
0 2 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1
三、数据误删恢复实操
3.1 完整恢复流程
sql
-- 场景:2024-06-15 14:30:00 误执行了 DROP TABLE orders
-- 前提:开启了 binlog
-- 步骤1:确认 binlog 文件和位置
SHOW MASTER STATUS;
-- 假设当前 binlog 文件:mysql-bin.000008
-- 步骤2:找到误操作前的 binlog 位置
SHOW BINLOG EVENTS IN 'mysql-bin.000008';
-- 找到含有 DROP TABLE 的事件,记录其 position
-- 步骤3:使用之前全量备份恢复
-- 先恢复最近一次全量备份(假设是 2024-06-15 凌晨 02:00)
mysql -u root -p < /backup/mysql/full_20240615/full_backup.sql
-- 步骤4:应用 binlog 增量(从备份完成到误操作前)
mysqlbinlog --start-datetime="2024-06-15 02:00:00" \
--stop-datetime="2024-06-15 14:29:59" \
mysql-bin.000008 mysql-bin.000007 | mysql -u root -p
-- 数据恢复到误删除前的状态!
3.2 误删数据恢复(没有备份的情况)
sql
-- 场景:误执行了 DELETE FROM orders WHERE status = 0(无 WHERE 条件)
-- 如果开启了 binlog 且格式为 ROW
-- 可以使用 mysqlbinlog 反解出原始数据
-- 步骤1:找到误操作对应的 binlog
mysqlbinlog --base64-output=decode-rows -vv \
--start-datetime="2024-06-15 14:00:00" \
mysql-bin.000008 > decoded.sql
-- 步骤2:从 ROW 格式的日志中找到 DELETE 事件的旧值
-- 文件中会有类似:
-- ### DELETE FROM `mydb`.`orders`
-- ### WHERE
-- ### @1=1 -- id
-- ### @2='20240101001' -- order_no
-- ### @3=100 -- user_id
-- ### ...
-- 步骤3:将 DELETE 转换为 INSERT
-- 可以写脚本将 DELETE 的旧值转为 INSERT 语句
-- 使用工具:mysqlbinlog 的 flashback 功能(第三方工具)
-- 或使用 binlog2sql 工具(Python)
pip install binlog2sql
binlog2sql --flashback \
-h127.0.0.1 -P3306 -uroot -p'password' \
-d mydb -t orders \
--start-file='mysql-bin.000008' \
--start-datetime='2024-06-15 14:00:00' \
--stop-datetime='2024-06-15 14:30:00' > flashback.sql
mysql -u root -p < flashback.sql
3.3 误删表的恢复
sql
-- 场景:DROP TABLE orders;
-- 方案1:从全量备份恢复 + 增量 binlog
-- 同上 3.1
-- 方案2:如果开启了延迟复制
-- 有一台延迟 1 小时的从库
-- 1. 停止从库同步
STOP SLAVE;
-- 2. 查看延迟了多少
SHOW SLAVE STATUS\G
-- Seconds_Behind_Master: 3600
-- 3. 手动应用 binlog 到误操作前的位置
START SLAVE UNTIL ...
-- 4. 导出误删的表
mysqldump -u root -p mydb orders > orders_recovery.sql
-- 5. 恢复到主库
mysql -u root -p mydb < orders_recovery.sql
四、实战案例:完整备份恢复策略
sql
-- 备份策略设计:
-- 1. 物理备份(xtrabackup)
-- 周日:全量备份
-- 周一~周六:增量备份
-- 2. 逻辑备份(mysqldump)
-- 每天凌晨:导出关键业务表
-- 3. Binlog 实时保存
-- 定期将 binlog 同步到备份服务器
-- 恢复能力:
-- - 全量+增量+binlog:恢复到任意时间点
-- - RTO(恢复时间目标):2 小时内
-- - RPO(恢复点目标):最多丢失 5 分钟数据
-- 验证备份有效性:
-- 定期(每月)做一次恢复演练:
-- 1. 在一台测试实例上恢复全量备份
-- 2. 应用增量备份
-- 3. 应用 binlog 到指定时间点
-- 4. 验证数据完整性
总结
| 日志/备份 | 核心要点 |
|---|---|
| Binlog 格式 | ROW 为主,STATEMENT/MIXED 了解 |
| Binlog 作用 | 主从复制、时间点恢复、审计 |
| 备份类型 | 物理备份(xtrabackup)快、逻辑备份(mysqldump)灵活 |
| 备份策略 | 全量+增量+binlog,定期验证恢复 |
| 恢复要点 | 全量恢复+binlog增量到误操作前 |
| 预防措施 | 延迟复制、权限控制、SQL审计 |