ThinkPHP项目定时备份数据库

wen PHP项目 3

本文目录导读:

ThinkPHP项目定时备份数据库

  1. 创建数据库备份类
  2. 创建定时任务控制器
  3. 注册命令
  4. 创建定时任务配置
  5. 使用 ThinkPHP 自带定时任务(可选)
  6. 创建备份管理控制器(后台管理)
  7. 邮件通知(可选)
  8. 注意事项:

我来提供一个完整的ThinkPHP项目定时备份数据库的解决方案:

创建数据库备份类

<?php
// app/common/library/DatabaseBackup.php
namespace app\common\library;
use think\facade\Db;
use think\facade\Config;
use think\Exception;
class DatabaseBackup
{
    /**
     * 备份文件存储路径
     */
    protected $backupPath;
    /**
     * 备份文件名前缀
     */
    protected $prefix = 'db_backup_';
    /**
     * 需要备份的表
     */
    protected $tables = [];
    /**
     * 需要跳过的表
     */
    protected $ignoreTables = [
        'sessions',
        'logs',
        'cache'
    ];
    public function __construct()
    {
        $this->backupPath = root_path() . 'backup' . DIRECTORY_SEPARATOR;
        if (!is_dir($this->backupPath)) {
            mkdir($this->backupPath, 0755, true);
        }
    }
    /**
     * 执行备份
     */
    public function backup()
    {
        try {
            // 获取所有表
            $tables = $this->getTables();
            $sql = "-- ThinkPHP Database Backup\n";
            $sql .= "-- Date: " . date('Y-m-d H:i:s') . "\n";
            $sql .= "-- Database: " . config('database.connections.mysql.database') . "\n\n";
            $sql .= "SET FOREIGN_KEY_CHECKS=0;\n\n";
            // 备份每个表
            foreach ($tables as $table) {
                if (in_array($table, $this->ignoreTables)) {
                    continue;
                }
                $sql .= $this->backupTable($table);
            }
            $sql .= "SET FOREIGN_KEY_CHECKS=1;\n";
            // 写入备份文件
            $filename = $this->backupPath . $this->prefix . date('Ymd_His') . '.sql';
            file_put_contents($filename, $sql);
            // 压缩文件
            $this->compress($filename);
            // 清理旧备份
            $this->cleanOldBackups();
            return [
                'status' => true,
                'message' => '数据库备份成功',
                'filename' => basename($filename),
                'size' => $this->formatSize(filesize($filename . '.gz'))
            ];
        } catch (Exception $e) {
            return [
                'status' => false,
                'message' => '备份失败: ' . $e->getMessage()
            ];
        }
    }
    /**
     * 获取所有数据表
     */
    protected function getTables()
    {
        $tables = Db::query('SHOW TABLES');
        $result = [];
        foreach ($tables as $table) {
            foreach ($table as $value) {
                $result[] = $value;
            }
        }
        return $result;
    }
    /**
     * 备份单个表
     */
    protected function backupTable($table)
    {
        $sql = "-- Table structure for table `{$table}`\n";
        $sql .= "DROP TABLE IF EXISTS `{$table}`;\n";
        // 获取建表语句
        $createTable = Db::query("SHOW CREATE TABLE `{$table}`");
        $sql .= $createTable[0]['Create Table'] . ";\n\n";
        // 获取表数据
        $sql .= "-- Data for table `{$table}`\n";
        $sql .= "INSERT INTO `{$table}` VALUES\n";
        $rows = Db::table($table)->select();
        $values = [];
        foreach ($rows as $row) {
            $values[] = "(" . $this->formatValues($row) . ")";
        }
        if (!empty($values)) {
            $sql .= implode(",\n", $values) . ";\n\n";
        } else {
            $sql = str_replace("INSERT INTO `{$table}` VALUES\n", "INSERT INTO `{$table}` VALUES ();\n", $sql);
        }
        return $sql;
    }
    /**
     * 格式化字段值
     */
    protected function formatValues($row)
    {
        $values = [];
        foreach ($row as $value) {
            if ($value === null) {
                $values[] = 'NULL';
            } elseif (is_numeric($value)) {
                $values[] = $value;
            } else {
                $values[] = "'" . mysqli_real_escape_string($this->getConnection(), $value) . "'";
            }
        }
        return implode(',', $values);
    }
    /**
     * 获取数据库连接
     */
    protected function getConnection()
    {
        return Db::connect()->getConnection();
    }
    /**
     * 压缩备份文件
     */
    protected function compress($filename)
    {
        $data = file_get_contents($filename);
        $gzdata = gzencode($data, 9);
        file_put_contents($filename . '.gz', $gzdata);
        unlink($filename); // 删除原始文件
    }
    /**
     * 清理旧备份
     */
    protected function cleanOldBackups($keepDays = 7)
    {
        $files = glob($this->backupPath . $this->prefix . '*.gz');
        $keepTime = time() - ($keepDays * 24 * 3600);
        foreach ($files as $file) {
            if (filemtime($file) < $keepTime) {
                unlink($file);
            }
        }
    }
    /**
     * 恢复数据库
     */
    public function restore($filename)
    {
        try {
            $filepath = $this->backupPath . $filename;
            if (!file_exists($filepath)) {
                throw new Exception('备份文件不存在');
            }
            // 解压文件
            $gzdata = file_get_contents($filepath);
            $sql = gzdecode($gzdata);
            // 执行SQL
            Db::execute($sql);
            return [
                'status' => true,
                'message' => '数据库恢复成功'
            ];
        } catch (Exception $e) {
            return [
                'status' => false,
                'message' => '恢复失败: ' . $e->getMessage()
            ];
        }
    }
    /**
     * 获取备份列表
     */
    public function getBackupList()
    {
        $files = glob($this->backupPath . $this->prefix . '*.gz');
        $list = [];
        foreach ($files as $file) {
            $list[] = [
                'filename' => basename($file),
                'size' => $this->formatSize(filesize($file)),
                'date' => date('Y-m-d H:i:s', filemtime($file))
            ];
        }
        // 按日期倒序排序
        usort($list, function($a, $b) {
            return strtotime($b['date']) - strtotime($a['date']);
        });
        return $list;
    }
    /**
     * 格式化文件大小
     */
    protected function formatSize($bytes)
    {
        if ($bytes >= 1073741824) {
            return number_format($bytes / 1073741824, 2) . ' GB';
        } elseif ($bytes >= 1048576) {
            return number_format($bytes / 1048576, 2) . ' MB';
        } elseif ($bytes >= 1024) {
            return number_format($bytes / 1024, 2) . ' KB';
        } else {
            return $bytes . ' B';
        }
    }
}

创建定时任务控制器

<?php
// app/command/BackupDatabase.php
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use app\common\library\DatabaseBackup;
class BackupDatabase extends Command
{
    protected function configure()
    {
        $this->setName('backup:database')
             ->setDescription('自动备份数据库');
    }
    protected function execute(Input $input, Output $output)
    {
        $backup = new DatabaseBackup();
        $result = $backup->backup();
        if ($result['status']) {
            $output->writeln("备份成功: " . $result['filename'] . " (" . $result['size'] . ")");
        } else {
            $output->writeln("备份失败: " . $result['message'] . PHP_EOL . $result['message']);
        }
        return 0;
    }
}

注册命令

config/console.php 中添加:

<?php
// config/console.php
return [
    'commands' => [
        'backup:database' => \app\command\BackupDatabase::class,
    ],
];

创建定时任务配置

Linux Cron 配置方式:

# 每天凌晨2点备份
0 2 * * * cd /path/to/your/project && php think backup:database >> /path/to/your/project/runtime/logs/backup.log 2>&1
# 每周一凌晨2点备份
0 2 * * 1 cd /path/to/your/project && php think backup:database >> /path/to/your/project/runtime/logs/backup.log 2>&1
# 每6小时备份一次
0 */6 * * * cd /path/to/your/project && php think backup:database >> /path/to/your/project/runtime/logs/backup.log 2>&1

Windows 计划任务配置:

创建 backup.bat 文件:

@echo off
cd /d D:\your\project\path
php think backup:database >> runtime\logs\backup.log 2>&1

使用 ThinkPHP 自带定时任务(可选)

如果你使用 ThinkPHP 6.x,可以配置计划任务:

<?php
// config/crontab.php
return [
    'tasks' => [
        [
            'name' => '数据库备份',
            'command' => 'backup:database',
            'schedule' => '* * * * *',
            'status' => 1,
        ],
    ],
];

创建备份管理控制器(后台管理)

<?php
// app/admin/controller/Backup.php
namespace app\admin\controller;
use think\Request;
use think\facade\View;
use app\common\library\DatabaseBackup;
class Backup
{
    protected $backup;
    public function __construct()
    {
        $this->backup = new DatabaseBackup();
    }
    /**
     * 备份列表
     */
    public function index()
    {
        $list = $this->backup->getBackupList();
        return View::fetch('backup/index', ['list' => $list]);
    }
    /**
     * 立即备份
     */
    public function create()
    {
        $result = $this->backup->backup();
        return json($result);
    }
    /**
     * 恢复备份
     */
    public function restore($filename)
    {
        $result = $this->backup->restore($filename);
        return json($result);
    }
    /**
     * 删除备份
     */
    public function delete($filename)
    {
        $filepath = root_path() . 'backup/' . $filename;
        // 安全检查
        if (strpos($filepath, '..') !== false || !file_exists($filepath)) {
            return json([
                'status' => false,
                'message' => '文件不存在'
            ]);
        }
        if (unlink($filepath)) {
            return json([
                'status' => true,
                'message' => '删除成功'
            ]);
        } else {
            return json([
                'status' => false,
                'message' => '删除失败'
            ]);
        }
    }
    /**
     * 下载备份
     */
    public function download($filename)
    {
        $filepath = root_path() . 'backup/' . $filename;
        if (!file_exists($filepath)) {
            return '文件不存在';
        }
        return download($filepath, $filename);
    }
}

邮件通知(可选)

// 在backup方法中添加邮件通知
protected function sendNotification($result)
{
    try {
        // 使用ThinkPHP邮件发送
        $content = "数据库备份" . ($result['status'] ? '成功' : '失败') . "\n";
        $content .= "时间: " . date('Y-m-d H:i:s') . "\n";
        if ($result['status']) {
            $content .= "文件名: " . $result['filename'] . "\n";
            $content .= "文件大小: " . $result['size'] . "\n";
        } else {
            $content .= "错误信息: " . $result['message'] . "\n";
        }
        // 发送邮件
        // Mail::send(...);
    } catch (\Exception $e) {
        // 记录日志
        \think\facade\Log::error('备份通知发送失败: ' . $e->getMessage());
    }
}

注意事项:

  1. 备份路径权限:确保 backup 目录有写权限
  2. 文件大小限制:数据库较大时请注意磁盘空间
  3. 安全防护:backup目录应设置访问限制
  4. 测试恢复:定期测试备份文件的恢复功能
  5. 日志记录:记录备份操作日志

这个方案提供了完整的数据库备份功能,包括自动备份、手动备份、恢复、下载等功能,可以直接集成到项目中。

抱歉,评论功能暂时关闭!