使用 Laravel 制定 MySQL 数据库备份计划任务

译文首发于 使用 Laravel 制定 MySQL 数据库备份计划任务,转载请注明出处。
你可以在终端里通过运行一行命令导出整个数据库。这种方案不仅简单直接而且有效。不过有更加自动化的解决方案。让我们来看看究竟是什么!

scheduling-mysql-backups-with-laravel

背景

几天前,我登录到错误的数据库中然后干掉了 18 000 行线上数据记录。更糟糕的是,我们没有对这个数据库进行备份。然后,我决定编写一个能够自动完成数据库导出并保存到 SQL 文件的脚本。

另外,如果你需要一款功能强大的数据备份系统,你可以看看 这个 扩展。这样我们就无需关注更多的数据库备份细节而仅需将焦点放到数据库导出和导出计划上。

导出命令

使用这个单行 snippet,你可以快速的将数据库导出到 SQL 文件。很多应用使用下面这个命令从数据库导出数据。

mysqldump -u[user] -p[pass] [db] > [file_path]

正如你所看到的那样,我们需要传入用户名、密码和需要导出的 DB,然后将输出重定向到指定的文件。食用简单方便,功效显著。

现在让我们将这个命令通过使用 artisan 命令进行封装,使其更易于运行和可加入计划任务。

Artiasn 控制台接口热身

通过使用 artisan 控制台(console)集成 shell 命令的一个重要出发点是,能够一次编写到处运行。我们要做的是配置并使用这些配置。这意味着,一旦有参数被修改,我们不需要通过命令本身进行调整。接下来,我们可以来创建这个控制台命令。

通过运行 php artisan make:comman 命令来创建一个自定义命令。这里我们的命令命名为 BackupDatabase。当创建完你的命令后,Laravel 会自动的将命令注册到系统中。你需要做的,仅仅是去定义命令的签名(signature)。

让我们来预览一下这个命令文件;稍后会详细解释它是如何运行的:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;

class BackupDatabase extends Command
{
    protected $signature = 'db:backup';

    protected $description = 'Backup the database';

    protected $process;

    public function __construct()
    {
        parent::__construct();

        $this->process = new Process(sprintf(
            'mysqldump -u%s -p%s %s > %s',
            config('database.connections.mysql.username'),
            config('database.connections.mysql.password'),
            config('database.connections.mysql.database'),
            storage_path('backups/backup.sql')
        ));
    }

    public function handle()
    {
        try {
            $this->process->mustRun();

            $this->info('The backup has been proceed successfully.');
        } catch (ProcessFailedException $exception) {
            $this->error('The backup process has been failed.');
        }
    }
}

你也看到了,我们的命令签名为 db:backup。由于 Laravel 已经有了 db 命令空间,这样命令更加清晰命令。

在构造函数中,我们实例化一个新的 Symfony\Component\Process\Process 实例。原因是这里我们需要使用 Symfony 的 Process 组件 - 而不是简单的调用 shell_exec 函数。这个组件提供了很多好赞的特性。比如,如果进程失败,我们可以抛出异常,然后有效处理异常。

如果你是用的是 process 的 run() 方法,你需要手动的去检测运行错误然后抛出异常。而通过 mustRun() 方法,它会自动的给我们抛出异常。你可以从 文档 中获取更多信息。

我们将 shell 命令和所需的参数传入到 sprintf() 函数中,它会将占位符替换成实际的参数。在处理完 process 实例后,我们可以进行下一步 handle)( 方法的处理。

在 handle 方法里,我们有个一 try-catch 代码块。首先,我们调用 mustRun() 方法,如果没有错误,我们向控制台中输出绿色的信息;否则,抛出 ProcessFailedException 异常,并在 catch 代码块中捕获,并向控制台中输出 error 信息。

接下来呢?如果我们在控制台执行 php artisan db:backup 命令,我们就会到此处数据库然后将其保存到 storage/backups/backup.sql 文件。运行良好,不过,我们还有一些工作要做,就是编写计划任务。

编写备份任务的计划任务

首先,在 Laravel 中能够轻松创建计划任务。它内置提供了既简单又支持链式操作的定义任务的 API 接口。在继续本文阅读之前,强烈建议阅读 它的文档 中译

然后,进入到 Console/Kernel.php 文件看看 schedule() 函数。我们可以定义任务和任务执行周期。比如,我们希望在 每周一的 23:00 运行计划,它的编码如下:

protected function schedule(Schedule $schedule)
{
    $schedule->command('db:backup')->mondays()->at('23:00');
}

是不是很简单?更棒的是,你可以在这里定义任意多个命令。调度器(scheduler)会在指定的时间分别处理这些任务。

若要运行这个调度器,我们需要执行 php artisan schedule:run 命令,然后它会触发所有需要运行的命令。这很棒,我们仅需一行命令就可以在指定的时间触发对应的任意命令。

但现在的问题时,如何管理调度器自身。这个有点像鸡生蛋蛋生鸡的问题,但是相信我,没有这么复杂。

使用 Forge 设置调度器

如果你还需要掌握 CORN 执行原理相关基础支持, Mohamed Said 有一个系列文章 深入讲解了 CRON 相关知识。其中关键点在于,我们无需为每个计划任务创建 CRON 定时器。我们仅需向前面介绍的那样定义任务执行手气,然后运行任务调取器就好了。

不过,我们需要设置运行 php artisan schedule:run 命令的时间。如果你使用了 Laravel Forge,那么可以很轻易的创建定时任务。只需进入到 Scheduler 选项卡,然后你就能创建任何你想要的计划任务。

forge schedule

如你所见,默认的已将添加了 schedule:run 命令,你需要做的就是,定义任务周期(frequency)以及替换默认命令到你服务器的命令。

如果准备好了,调度器将每次在适当的时候运行,并触发所有要执行的命令。

总结

很高兴; 我们可以提供轻量级的解决方案,而不依赖于一个更大的包。在这里,我们也可以利用 Laravel 的优势来满足需求。

我们可以使用 Process 组件轻松导出数据库,并将其封装在 artisan 命令中。然后,我们可以快速地为我们的命令设置一个执行周期,而 Laravel 的调度程序将负责剩下的工作。我们可以躺着就把活该干了。

原文

Scheduling MySQL Backups with Laravel

本帖由系统于 6个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4
try {
            // 删除N天之前的备份
            $directory = storage_path('backups/');
            $files     = scandir($directory);

            foreach ($files as $file) {
                if (!in_array($file, ['.', '..']) && Carbon::now()->diffIndays(substr($file, 7, 10)) >= env('DB_BACKUP_DAYS', 5)) {
                    unlink($directory . $file);
                }
            }

            $this->process->mustRun();

            $this->info('The backup has been proceed successfully.');
        } catch (ProcessFailedException $exception) {
            $this->error('The backup process has been failed.');
        }
7个月前

想请问一下 Symfony 的 Process 组件这个是一个什么样的东西,是一个拓展包还是laravel本身自带

6个月前
liuqing_hu

@狗达 这个是 Symfony 框架的组件,你可以到 Github 上搜索这个框架

6个月前

Artiasn 控制台接口热身,英文拼写错了

5个月前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!