MySQL 数据库是在 VPS 上运行的大多数应用程序的支柱 — 无论是 WordPress、Laravel、Node.js 还是电子商务平台。数据库内的数据是无法替代的,丢失数据可能意味着数小时甚至数天的停机时间和可能的永久数据丢失。了解如何可靠地备份和正确还原是任何 VPS 管理员最重要的技能之一。本指南将为您详细介绍所有内容:从使用 mysqldump 的简单手动备份到完全自动化的夜间 cron 任务,包含可立即使用的真实命令。
为什么 VPS 上的数据库备份很重要
与共享主机不同,共享主机上提供商通常处理服务器级别的快照,而 VPS 将责任完全推给您。您拥有完整的 root 访问权限,这意味着完整的责任。VPS 上数据丢失的最常见原因包括:
- 人为错误 — 运行
DROP TABLE或没有WHERE子句的DELETE比您想象的更常见,即使在经验丰富的开发人员中也是如此。 - 硬件故障 — 虽然罕见,但虚拟化基础设施上的基础磁盘故障可能会损坏或销毁数据文件。
- 勒索软件/网络攻击 — 暴露的 MySQL 端口或弱凭证可能导致自动程序加密或擦除数据。
- 软件更新失败 — 失败的框架迁移或有问题的插件更新可能会无意中更改架构或删除行。
- 磁盘已满 — 当磁盘完全填满时,MySQL 可能会损坏正在活跃写入的表。
行业标准的 3-2-1 规则适用于此:保留 3 份数据副本,分别存储在 2 种不同的存储类型上,1 份副本存储在站点外。至少,您应该有本地每日备份和至少每周上传到云存储等远程位置。
MySQL 备份工具对比
MySQL 提供多个备份实用程序,每个都适合不同的场景。选择正确的工具取决于您的数据库大小、存储引擎以及您能容忍的停机时间。
| 工具 | 类型 | 表锁定 | 最佳用途 |
|---|---|---|---|
| mysqldump | 逻辑(SQL 文本) | 无锁定(InnoDB + --single-transaction) |
DB < 5 GB,易于移植 |
| mysqlpump | 逻辑(并行) | 无锁定(InnoDB) | 多个数据库,> 1 GB,更快 |
| Percona XtraBackup | 物理(文件副本) | 热备份,无锁定 | 大型生产数据库 |
| mysqlhotcopy | 物理(已弃用) | 复制期间锁定表 | 不推荐(仅限旧版 MyISAM) |
对于大多数 VPS 用户,mysqldump 是最好的起点。它预装在 MySQL 中,不需要任何额外的包,并生成可读的 SQL 文件,您可以在任何兼容的 MySQL 实例上检查、修改或还原。
使用 mysqldump 备份
mysqldump 将您的数据库结构和数据导出到包含 CREATE TABLE 和 INSERT 语句的 .sql 文件中。此文件可以在任何具有相同或更新版本的 MySQL 服务器上还原。
备份单个数据库
mysqldump -u root -p mydb > /backup/mydb_$(date +%Y%m%d).sql
为了节省磁盘空间,可以使用 gzip 实时压缩输出:
mysqldump -u root -p mydb | gzip > /backup/mydb_$(date +%Y%m%d).sql.gz
InnoDB 一致性备份(无表锁定)
--single-transaction 标志对于实时数据库至关重要。它告诉 mysqldump 在读取数据前开始一个一致的快照事务,这样应用程序可以在备份过程中继续写入,无需停机:
mysqldump -u root -p \ --single-transaction \ --routines \ --triggers \ --events \ mydb | gzip > /backup/mydb_$(date +%Y%m%d_%H%M).sql.gz
--routines、--triggers 和 --events 标志确保存储过程、触发器和计划事件被包含在备份中 — 这对完整还原至关重要。
一次备份所有数据库
mysqldump -u root -p \ --all-databases \ --single-transaction \ --routines \ --triggers \ | gzip > /backup/all_databases_$(date +%Y%m%d).sql.gz
仅模式备份(不含数据)
当你想要将表结构克隆到开发服务器而不复制生产数据时很有用:
mysqldump -u root -p --no-data mydb > /backup/mydb_schema_only.sql
还原 MySQL 数据库
还原必须小心执行。始终在实际紧急情况发生前在暂存环境中测试还原程序 — 从未测试过的备份不是真正的备份。
从纯 SQL 文件还原
# Step 1: Create the database if it doesn't exist mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # Step 2: Restore mysql -u root -p mydb < /backup/mydb_20260609.sql
从压缩文件还原 (.sql.gz)
gunzip -c /backup/mydb_20260609.sql.gz | mysql -u root -p mydb
另外,可以使用 zcat,它在某些系统上可能速度稍快:
zcat /backup/mydb_20260609.sql.gz | mysql -u root -p mydb
带进度跟踪的还原(大文件)
对于大型备份文件,安装 pv(管道查看器)来查看实时进度:
# Install pv if not already installed apt install pv -y # Restore with progress indicator pv /backup/mydb_20260609.sql.gz | gunzip | mysql -u root -p mydb
仅还原特定表
当只有一个表损坏时,你可以仅转储和还原该表而不是整个数据库:
# Dump a specific table mysqldump -u root -p mydb users > /backup/users_table.sql # Restore just that table mysql -u root -p mydb < /backup/users_table.sql
专业提示:将 MySQL 凭证安全地存储在 ~/.my.cnf 而不是在命令行上输入(这会在 shell 历史记录中记录):[client]
user=root
password=YourPassword
立即设置严格权限:chmod 600 ~/.my.cnf — 然后可以运行 mysqldump 和 mysql 而无需 -p 标志。
使用 Cron 自动化备份
手动备份总比没有好,但自动化计划备份才是真正保护生产数据的方式。编写良好的 cron 作业每晚自动运行,无需人工干预。
创建备份脚本
创建文件 /usr/local/bin/mysql-backup.sh:
#!/bin/bash
# MySQL Automated Backup Script
# AsiaGB VPS Guide — 2026-06-09
BACKUP_DIR="/backup/mysql"
MYSQL_USER="root"
MYSQL_PASS="YourSecurePassword"
KEEP_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Dump all databases with compression
mysqldump -u "$MYSQL_USER" -p"$MYSQL_PASS" \
--all-databases \
--single-transaction \
--routines \
--triggers \
| gzip > "$BACKUP_DIR/all_db_${DATE}.sql.gz"
# Log result
if [ $? -eq 0 ]; then
echo "[$(date)] SUCCESS: all_db_${DATE}.sql.gz" >> /var/log/mysql-backup.log
else
echo "[$(date)] ERROR: Backup failed!" >> /var/log/mysql-backup.log
fi
# Remove backups older than KEEP_DAYS
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$KEEP_DAYS -delete
echo "[$(date)] Cleaned up backups older than $KEEP_DAYS days" >> /var/log/mysql-backup.log
使脚本可执行:
chmod +x /usr/local/bin/mysql-backup.sh
添加 Cron 任务
crontab -e
添加以下这行,让备份每晚 02:00 自动执行:
# Nightly MySQL backup at 02:00 0 2 * * * /usr/local/bin/mysql-backup.sh
如需更高频率的备份(每 6 小时一次):
0 */6 * * * /usr/local/bin/mysql-backup.sh
验证 Cron 任务是否正常运行
# Check the backup log tail -f /var/log/mysql-backup.log # List backup files ls -lh /backup/mysql/ # Test the script manually /usr/local/bin/mysql-backup.sh
使用 mysqlpump 备份大型数据库
mysqlpump(带字母"p")在 MySQL 5.7 中引入,支持多线程并行导出,对大型数据库的速度远快于 mysqldump。
# Backup with mysqlpump using 4 parallel threads mysqlpump -u root -p \ --default-parallelism=4 \ --compress-output=LZ4 \ --all-databases \ > /backup/all_db_pump_$(date +%Y%m%d).sql # Backup selected databases only mysqlpump -u root -p \ --default-parallelism=4 \ --include-databases=mydb,mydb2 \ > /backup/selected_db_$(date +%Y%m%d).sql
mysqlpump 相比 mysqldump 的主要优势:
- 并行导出显著缩短多核服务器上的备份时间
- 内置进度指示器,转储过程中实时显示完成百分比
- 原生支持多种压缩算法(LZ4、ZLIB),无需外部工具
- 对数据库和表提供更细粒度的包含/排除选项
- 得益于更高的压缩效率,输出文件通常更小
备份安全与文件管理
MySQL 备份文件包含整个数据库的 SQL 明文内容,保护这些文件与创建备份本身同等重要。
设置正确的文件权限
# Backup directory: readable only by root chmod 700 /backup/mysql chown root:root /backup/mysql # Individual backup files: readable only by root chmod 600 /backup/mysql/*.sql.gz
验证备份完整性
每次备份完成后,验证文件未损坏且转储已成功完成:
# Test that the gz file is not corrupted gunzip -t /backup/mysql/all_db_20260609.sql.gz && echo "File OK" # Check the last few lines of the dump for "Dump completed" gunzip -c /backup/mysql/all_db_20260609.sql.gz | tail -5
使用 rclone 进行异地备份
要实现真正的灾难恢复,至少每周将备份发送到云存储等异地位置。使用 rclone 可以轻松对接任何 S3 兼容存储、Google Drive 或 Backblaze B2:
# Example: sync backup folder to S3 rclone copy /backup/mysql/ s3:mybucket/mysql-backup/ --min-age 1h # Add to cron to run 30 minutes after the local backup 30 2 * * * rclone copy /backup/mysql/ s3:mybucket/mysql-backup/ --min-age 1h
常见问题排查
即使命令看似简单,备份和还原操作也可能以可预见的方式失败。以下是最常见的问题及解决方法。
错误:用户 'root'@'localhost' 访问被拒绝
这通常是凭证错误或用户权限不足导致的。建议创建专用备份用户以提高安全性:
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'StrongPassword'; GRANT SELECT, LOCK TABLES, SHOW VIEW, TRIGGER, EVENT ON *.* TO 'backup_user'@'localhost'; FLUSH PRIVILEGES;
还原大型数据库时速度极慢
可以通过在 /etc/mysql/my.cnf 中添加以下设置临时调优 MySQL,加快导入速度:
[mysqld] innodb_buffer_pool_size = 2G innodb_log_file_size = 512M innodb_flush_log_at_trx_commit = 2 sync_binlog = 0
同时,在还原会话中禁用外键检查可进一步加速:
mysql -u root -p mydb <<EOF SET FOREIGN_KEY_CHECKS=0; SET UNIQUE_CHECKS=0; SOURCE /backup/mydb_20260609.sql; SET FOREIGN_KEY_CHECKS=1; SET UNIQUE_CHECKS=1; EOF
生产环境 MySQL 备份最佳实践
健全的备份策略不仅仅是每天跑一个 cron 任务。以下是生产部署中值得额外考量的做法:
- 每次发布前先备份 — 在执行数据库迁移或部署新代码之前,手动备份一次,作为即时的安全保障。
- 每月测试还原 — 从未还原过的备份是未经证实的。每月启动一台测试服务器并还原最新备份,确认其真实可用。
- 监控备份文件大小 — 如果今天的备份文件比昨天小得多,可能意味着转储不完整。在脚本中添加基于文件大小的告警。
- 启用二进制日志 — MySQL 二进制日志支持时间点恢复(Point-in-Time Recovery),可以将数据库精确还原到数据丢失前的那一秒。
- 备份失败时及时告警 — cron 脚本应在退出码非零时通过邮件或消息通知您。无声失败是最危险的情况。
- 加密敏感备份 — 对于包含个人数据的数据库,考虑在上传到远程存储前使用 GPG 对备份文件进行静态加密。
常见问题解答(FAQ)
mysqldump 和 mysqlpump 有什么区别?
mysqldump 是传统的单线程工具,以纯文本形式导出 SQL,适合中小型数据库。mysqlpump 是较新的工具,支持多个数据库同时并行导出,内置进度指示器,且能原生压缩输出内容。对于超过 1 GB 的数据库,或需要同时备份多个数据库的场景,推荐使用 mysqlpump。
还原 MySQL 数据库后,部分数据丢失,应该检查哪些方面?
还原后数据丢失,请检查三个主要方面:第一,字符集不匹配 — 转储文件可能使用 utf8mb4,而目标数据库为 utf8,请检查 .sql 文件中的 CHARACTER SET 声明。第二,外键约束导致事务回滚 — 在导入前添加 SET FOREIGN_KEY_CHECKS=0,导入后重新启用。第三,转储不完整 — 检查 .sql 文件最后几行是否包含"Dump completed on"字样来验证备份是否完整。
如何使用 cron 设置自动夜间 MySQL 备份?
使用 crontab -e 打开 crontab 并添加:0 2 * * * /usr/bin/mysqldump -u root -p'YOURPASSWORD' --all-databases | gzip > /backup/db_$(date +\%Y\%m\%d).sql.gz — 这会在每晚 02:00 运行并使用 gzip 压缩输出。将备份文件存放在网站根目录之外,并使用 find /backup -name '*.sql.gz' -mtime +30 -delete 自动清除超过 30 天的旧文件。
如何加快大型 MySQL 数据库的备份速度?
以下几种方法可以提升大型数据库的备份速度:第一,使用 mysqlpump 替代 mysqldump,并设置 --default-parallelism=4 启用多线程并行导出。第二,对 InnoDB 使用 Percona XtraBackup,作为无需表锁的热备份方案。第三,在 mysqldump 中添加 --single-transaction,实现一致的 InnoDB 备份而不锁表。第四,通过管道将输出压缩为 gzip 或 zstd 格式以减少 I/O 时间。第五,按数据库或表分别备份,而非一次性转储所有内容。