先定策略:逻辑备份 vs 物理备份,何时选哪边
mysqldump 属于逻辑备份:导出的是 SQL 语句,跨版本、跨平台、可部分恢复,但全量恢复慢于物理备份(xtrabackup 的裸文件拷贝)。小型库和排障场景用逻辑备份足够,几十 GB 以上的在线库更要考虑物理快照。
这里给出的是逻辑备份的完整链路,默认假设你已经清楚库里没有独享的存储过程依赖冲突,或者通过 --routines 显式带上。备份的黄金法则是:每天全量 + binlog 归档,让恢复点能精细到分钟。
# 单库全量并带 事件/触发器/存储过程
mysqldump -u root -p --single-transaction --routines --events \
--databases me_db > /backup/me_db.$(date +%F).sql
# 校验导出非空且有 HEADER
grep -c "CREATE TABLE" /backup/me_db.$(date +%F).sql
head -5 /backup/me_db.$(date +%F).sql一致性是关键:--single-transaction 与 MyISAM 的取舍
默认 mysqldump 会对表加锁,InnoDB 下你想无锁一致快照,就上 --single-transaction:它在单个 REPEATABLE READ 事务里导出所有表,保证看到一致的时间点。但它依赖 InnoDB 的 MVCC,对 MyISAM 不生效,MyISAM 仍需 --lock-tables。
混合引擎的库要小心:--single-transaction 只保证 InnoDB 一致,MyISAM 表仍会被临时锁。若你的表全在 InnoDB(推荐状态),可以放心用单事务导出,还能兼 --master-data=2 记录位点,方便后续接 binlog 恢复。
InnoDB 推荐
mysqldump -u root -p --single-transaction --master-data=2 \
--routines me_db > me_db.$(date +%F).sql
# 混用引擎时至少带上锁
mysqldump -u root -p --lock-tables --routines me_db > me_db_lock.sql
# 看记录下的 binlog 位点
grep -m1 "CHANGE MASTER" me_db.$(date +%F).sql恢复第一步:导入前的纪律与加速参数
导入前先做三件事:确认目标库存在(缺失时 mysql -e "CREATE DATABASE")、关闭外键检查、关闭自动提交。对 InnoDB 还建议临时调高 buffer pool 的大小,让大量 index 操作加速。
导入本身用 mysql < dump.sql 而非过分拆分的循环插入。大量单条 INSERT 是恢复慢的头号原因,mysqldump 可以 --opt 前缀把多条合并,恢复端再配 innodb_flush_log_at_trx_commit=0 与 autocommit off,能显著缩短导入时间。
# 错误示范:每行一条提交,慢到怀疑人生
# mysql me_db < dump.sql # 无外键关闭也无缓存池
# 修复对照:先做工作区再导
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS me_db"
mysql -u root -p me_db < dump.sql && echo "seated OK" || echo "import returned non-zero"
# 加速用:临时降低 flush 强度
mysql -u root -p -e "SET GLOBAL innodb_flush_log_at_trx_commit=0; SET autocommit=0;"误删恢复:从全量 + binlog 精确回到删除前那一刻
误删(update 少写 WHERE、DELETE 忘带条件)是最痛的操作事故,靠"全量+binlog 重放"补救:先把全量导入到一个临时库,再用 --stop-position 或 --stop-datetime 重放 binlog,直到误操作前的最后一条事务,砍向正确的时间点。
前提是 binlog 已开(log_bin=ON)且 command 里有 row 或 mixed 格式。操作节奏:先定位误删 binlog 的坐标(用 mysqlbinlog 找那条 DEL/UPDATE),划定停止位点,导入到临时库核对,再按需切回主库。下面给出一整套流程。
# 1) 找到误删事件
mysqlbinlog --no-defaults --skip-opt --base64-output=decode-rows \
/var/lib/mysql/bin.000044 | grep -n "DELETE FROM" | head
# 2) 重放到指定时刻之前
mysqlbinlog --no-defaults --stop-datetime="2026-09-06 10:15:00" \
/var/lib/mysql/bin.000044 | mysql -u root -p stage_db
# 3) 核对 row count 后切回
mysql -u root -p -e "SELECT COUNT(*) FROM stage_db.orders"
# 确认无误再用一片事务导入真正表:
mysql -u root -p me_db < stage_clean.sql错误示范与修复:表面上成功了其实没恢复内容
导入脚本很常见地显示成功,但目标表是空的或者缺行。根因通常是三条:导入了错误的(旧的)dump 文件、目标库存在同名表但 dump 里没有 DROP TABLE 导致被跳过、或 FK 顺序让导入中断在本应先插入的外键子表上。
把"成功"改成"可验证的成功":导入后用新库全形重放并统计 row count 对照源库,或者把 dump 文件先跑一遍 --force 抓告警。正规检视是 `awk "NR==FNR..."` 对比两张表行数,下面给出校验脚本 + 修复要点。
# 错误示范:默认产出会在导入前自动 DROP,若目标已有表则可能跳过
# 修复对照:追加 --add-drop-table 保证重建,或先清空旧库
mysqldump --add-drop-table me_db > me_db_clean.sql
# 还原后用行数对账
mysql -u root -p -N -e "SELECT (SELECT COUNT(*) FROM src.orders) AS s, (SELECT COUNT(*) FROM stage.orders) AS t"
# 找出库里缺失的表
mysql -u root -p -N -e "SELECT table_name FROM information_schema.tables WHERE table_schema='stage'"