最稳、最可控的MySQL备份方式是直接用mysqldump配合Yii控制台命令,而非第三方扩展;因扩展常将SQL文件存于Web可访问目录、硬编码已废弃函数、绕过Yii DB配置导致权限错误与密码泄露风险。

直接用 mysqldump + Yii 控制台命令是最稳、最可控的方式,别迷信“一键备份扩展”,多数封装反而掩盖权限、路径、字符集问题,出错时连日志都难定位。
为什么不用 spanjeta/yii2-backup 这类扩展
这类扩展默认把备份文件写进 @web/sql 或 @app/web/backups,但 Web 目录可被直接 HTTP 访问——SQL 文件一旦生成,攻击者可能通过 URL 下载全库数据;它还硬编码了 mysql_real_escape_string()(PHP 7.0+ 已移除),不兼容新版环境;更关键的是,它绕过 Yii 的 DB 组件连接参数,手动拼接用户名密码,容易泄露敏感信息到进程列表(ps aux 可见明文密码)。
常见错误现象:ERROR 1045 (28000): Access denied for user —— 因为扩展没读取 Yii 配置里的 db 组件,而是用空密码或旧配置硬连。
- 真正要用扩展,优先选
e282486518/yii2-console-migration,它基于 migration 机制,备份结果是标准 PHP migration 文件,可版本管理、可回滚 - 若必须用 SQL 文件备份,自己写控制台命令,全程走 Yii 的
Yii::$app->db配置,避免密码明文暴露 - 所有备份脚本必须运行在
console环境下,不能放在web控制器里——否则超时、内存溢出、无权写磁盘等问题频发
exec('mysqldump') 的安全写法与坑点
直接调用 mysqldump 不等于裸奔,关键是隔离敏感参数、控制输出、捕获错误。
正确示例(放在 console/controllers/BackupController.php 中):
public function actionFull()
{
$db = Yii::$app->db;
$dumpPath = Yii::getAlias('@app/runtime/backups/');
if (!is_dir($dumpPath)) {
mkdir($dumpPath, 0755, true);
}
$filename = "full_{$db->dsn}_".date('Ymd_His').'.sql';
$filepath = $dumpPath . $filename;
<pre class="brush:php;toolbar:false;">// 用 --defaults-file 避免密码出现在 ps 输出中
$defaultsFile = tempnam(sys_get_temp_dir(), 'my.cnf');
file_put_contents($defaultsFile, "[client]\nuser={$db->username}\npassword={$db->password}\n");
chmod($defaultsFile, 0600);
$cmd = "mysqldump --defaults-file={$defaultsFile} --single-transaction --routines --triggers {$db->dbname} > {$filepath}";
$output = [];
$returnCode = 0;
exec($cmd . ' 2>&1', $output, $returnCode);
unlink($defaultsFile); // 立即删掉临时配置
if ($returnCode !== 0) {
throw new Exception('Backup failed: ' . implode("\n", $output));
}
echo "Backup saved to {$filepath}\n";}
- 绝对不要拼接
-u xxx -p yyy:密码会出现在ps aux和系统进程树中,生产环境严禁 - 务必加
--single-transaction:保证 InnoDB 表一致性,避免锁表 -
@app/runtime/backups/是推荐路径——不在 Web 可访问范围内,且 Yii 默认有写权限 - 执行后检查
$returnCode,仅靠file_exists()判断成功是错的:mysqldump可能中途失败但仍生成空文件
定时任务配 crontab 的三个硬性条件
Linux 上跑定时备份,以下三点缺一不可,否则 cron 里静默失败、无日志、不报警。
- 完整绝对路径:crontab 里必须写
/usr/bin/php /var/www/myapp/yii backup/full,不能用php yii ...—— cron 的$PATH通常不含/usr/local/bin - 显式指定环境变量:在 crontab 条目前加
SHELL=/bin/bash和PATH=/usr/local/bin:/usr/bin:/bin,否则mysqldump可能找不到 - 重定向输出:末尾加
> /var/www/myapp/runtime/logs/backup.log 2>&1,否则 cron 错误只发邮件(多数服务器没配 mail 服务)
典型 crontab 条目:
0 2 * * * SHELL=/bin/bash PATH=/usr/local/bin:/usr/bin:/bin /usr/bin/php /var/www/myapp/yii backup/full >> /var/www/myapp/runtime/logs/backup.log 2>&1
还原操作必须离线、分步验证
备份只是第一步,还原才是容灾能力的试金石。线上库绝不能直接 mysql 。
- 先在测试库还原:
mysql -u root -p test_db ,确认语法无错、字符集不乱码、外键约束正常 - 再检查关键表行数是否匹配:对比
SELECT COUNT(*) FROM user;在原库和还原库的结果 - 最后才考虑线上恢复——且必须停写入、锁表、记录 binlog 位置,否则主从同步可能断裂
最容易被忽略的一点:mysqldump 默认不导出 CREATE DATABASE 语句,还原前要手动建库,且库字符集需与原库一致(比如 CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;)。漏掉这步,中文字段大概率变问号。


















