核心需求
- 導出指定數據庫所有表結構(CREATE TABLE)
- 導出所有表數據(INSERT 语句)
- 生成單一 .sql 文件供下載
- 執行完清理臨時文件
完整實現代碼
<?php
// 配置
$database = 'mydb';
$config = [
'host' => 'localhost',
'user' => 'backup_user',
'pass' => 'backup_pass',
'charset' => 'utf8mb4',
'filename' => $database . '_' . date('Ymd_His') . '.sql'
];
// 連接
$mysqli = new mysqli($config['host'], $config['user'], $config['pass'], $database);
if ($mysqli->connect_errno) {
die("連接失敗: {$mysqli->connect_error}");
}
$mysqli->set_charset($config['charset']);
// 獲取所有表
$tables = [];
$res = $mysqli->query("SHOW TABLES");
while ($row = $res->fetch_row()) $tables[] = $row[0];
$res->free();
// 打開輸出文件
$fp = fopen($config['filename'], 'w');
if (!$fp) die('無法創建輸出文件');
// 寫入文件頭
fwrite($fp, "-- MySQL 備份文件\n");
fwrite($fp, "-- 數據庫: {$database}\n");
fwrite($fp, "-- 生成時間: " . date('Y-m-d H:i:s') . "\n\n");
fwrite($fp, "SET FOREIGN_KEY_CHECKS=0;\n\n");
foreach ($tables as $table) {
// 1. 導出表結構
$createRes = $mysqli->query("SHOW CREATE TABLE `{$table}`");
$createRow = $createRes->fetch_assoc();
fwrite($fp, "-- 表結構: {$table}\n");
fwrite($fp, "DROP TABLE IF EXISTS `{$table}`;\n");
fwrite($fp, $createRow['Create Table'] . ";\n\n");
$createRes->free();
// 2. 導出數據
$dataRes = $mysqli->query("SELECT * FROM `{$table}`");
if ($dataRes->num_rows > 0) {
fwrite($fp, "-- 數據: {$table}\n");
while ($row = $dataRes->fetch_assoc()) {
$cols = array_keys($row);
$vals = array_map(function($v) use ($mysqli) {
return $v === null ? 'NULL' : "'" . $mysqli->real_escape_string($v) . "'";
}, $row);
fwrite($fp, "INSERT INTO `{$table}` (`" . implode('`, `', $cols) . "`) VALUES (" . implode(', ', $vals) . ");\n");
}
fwrite($fp, "\n");
}
$dataRes->free();
}
fwrite($fp, "SET FOREIGN_KEY_CHECKS=1;\n");
fclose($fp);
// 下載輸出
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($config['filename']) . '"');
header('Content-Length: ' . filesize($config['filename']));
readfile($config['filename']);
// 清理
unlink($config['filename']);
$mysqli->close();
exit;
關鍵點解析
| 環節 | 處理方式 | 原因 |
| 表結構 | SHOW CREATE TABLE | 獲取完整建表語句含索引、引擎、字符集 |
| 數據轉義 | mysqli_real_escape_string | 防止特殊字符破壞 SQL 語法 |
| NULL 處理 | 顯式輸出 NULL 而非空字符串 | 保持數據語義準確 |
| 外鍵約束 | SET FOREIGN_KEY_CHECKS=0/1 | 導入時避免順序依賴報錯 |
| 內存控制 | 逐行寫入文件,不載入全量數據 | 支援大數據庫不 OOM |
增強版功能建議
- 支援壓縮輸出:
gzopen 寫入 .sql.gz
- 支援單表/多庫導出參數化
- 添加
--single-transaction 邏輯(需 SELECT 權限)
- 集成到控制台命令(Symfony Console / Laravel Artisan)
- 上傳至雲存儲(S3/OSS)而非本地下載
對比 mysqldump
| 維度 | PHP 實現 | mysqldump |
| 性能 | 較慢(PHP 解析開銷) | 極快(C 級別、流式) |
| 可定制 | 極高(業務邏輯可介入) | 參數固定 |
| 部署依賴 | 僅需 PHP mysqli | 需 mysqldump 二進制、權限 |
| 適用場景 | Web 界面手動導出、特殊過濾 | 定時自動備份、大數據庫 |
總結
PHP 導出 .sql 適用於「Web 界面按需導出、需業務過濾、無服務器 Shell 權限」場景。生產級定時備份仍首選 mysqldump --single-transaction --routines --triggers --events 配合壓縮上傳。代碼核心在於:逐表結構+數據流式寫入、完整轉義、外鍵暫用、內存可控。