MySQLをPHPとcronで定期バックアップ

MySQLをPHPとcronで定期バックアップ

レンタルサーバーには必ず(だと思う…)バックアップ機能があるとは思いますが、いざというときには自作で用意しておいたほうが早い対応が可能になります(変わらないか(笑)…)。

サーバーの cron で動かす PHP スクリプトを書いてみました。簡単でした。

01MySQL をデータベースごとにダンプする PHP スクリプト

まずそのスクリプトを。

なお、XServer のデータベース仕様で書いていますので他のレンタルサーバーの場合は一部修正が必要になります。

<?php
/*
 * データベースを各データベースごとバックアップするスクリプト
 * 保存ファイル名は Ymd-データベース名.sql のため1日1回、上書きされる
 * 1日に数回実行する場合は YmdHi 等ファイル名に時間を付加する
 * 保存指定日数以前のファイルは削除する(デフォルト 3日)
 * Xserver での仕様を前提としているためデータベース名は サーバーID_データベース名 となる
 */

// バックアップファイルを残す過去日数
$keep_days=3;

// データベースデータ
$host='localhost';
$user='ユーザー名';
$password='パスワード';

// 保存場所
$save_dir='./';
// バックアップファイルに日付付加
$now=date('Ymd');
$old=date('Ymd', strtotime('-' . $keep_days . ' day'));

// ホスト内のデータベース名取得
try {
    $mysqli = new mysqli($host, $user, $password);
    $result = $mysqli->query('SHOW DATABASES');
} catch (mysqli_sql_exception $e) {
    error_log("Connect failed: " . $e->getMessage() . "\n", 3, "error" . $now . ".log");
    exit;
}

// 各データベースごとバックアップを保存し、保存期間を過ぎたファイルを削除
while ($row = $result->fetch_assoc()) {
    $flg = preg_match('/サーバーID_(.*)/', $row['Database'], $matches);
    if($flg){
        $cmd = 'mysqldump --single-transaction --quick'
            . ' -h ' . $host
            . ' -u ' . $user
            . ' -p' . $password
            . ' ' . $matches[0]
            . ' > ' . $save_dir . $now . '-' . $matches[1] . '.sql';
        exec($cmd);
        $oldfile = $old . '-' . $matches[1] . '.sql';
        if (file_exists($save_dir . $oldfile)){
            unlink($save_dir . $oldfile);
        }
    }
}

$mysqli->close();

02スクリプトの説明

mysql_connect 等 PHP の MySQL 関数は PHP 5.5(2013年6月リリース)から非推奨とされていますし、PHP 7.0(2015年12月リリース)からは削除されています。MySQLi あるいは PDO_MySQL を使うよう推奨されています。下記リンクを参照してください。

仕様

XServer のデータベース仕様になっています。

  • データベースは localhost 内に「サーバーID_データベース名」で保存される
    たとえば xs123456_hogehoge となり hogehoge は任意となっている
  • バックアップファイルの保存場所はスクリプトと同じディレクトリ内
  • バックアップファイル名は「保存日-データベース名.sql」
    たとえば 20250319-hogehoge.sql
  • 保存日数(デフォルト3日)以前のファイルは削除される

他のレンタルサーバー仕様に変更する場合は、

  • 31行目の preg_match の正規表現のパターンを変更する
  • 14行目の $host を確認する

解説

mysqldump は –all-databases オプションにより全てのデータベースをダンプすることもできますが、これですとひとつのファイルになってしまいリストアに不便ですので、まず存在するデーターベースを取得してそれぞれのデータベース名でダンプして保存します。

データベース名は次のコードで取得できます。

$mysqli = new mysqli(ホスト名, ユーザー名, パスワード);
$result = $mysqli->query('SHOW DATABASES');
while ($row = $result->fetch_assoc()) {
    echo $row['Database'];
}

エラーが出た場合はスクリプトと同じディレクトリに error-日付.log ファイルを残します。

問題なくデータベースに接続できた場合はデータベース作成時につけた任意のデータベース名を取り出します。取得したデータベース名そのままをファイル名にしてもいいのですが、サーバーID が鬱陶しいですので

preg_match('/サーバーID_(.*)/', $row['Database'], $matches);

として、matches[0] にデータベース名($row[‘Database’] と同じ)、matches[1] に自分でつけた任意のデータベース名を取り出しています。

続いて mysqldump コマンドの作成です。オプションについては

を参照してください。

スクリプトと同じディレクトリ内に各データベースが「保存日-データベース名.sql」として保存されます。

そして最後に保存指定日数を過ぎたファイルを削除します。

03サーバーでの cron 設定

スクリプトをファイルマネージャーなどでサーバーにアップロードします。ホームディレクトリなどウェブからアクセスできない場所に置きます。

続いてサーバーのコントロールパネルに cron 設定があると思いますので1日1回希望の時間にスクリプトが稼働するよう設定します。

XServer の例です。

私は毎日早朝の4時に稼働するよう「0 4 * * *」としています。

コマンドは次のとおりです。

cd /home/サーバーID/backup && /usr/bin/php8.1 ./mysql-backup.php
または
/usr/bin/php8.1-cgi /home/サーバーID/backup/mysql-backup.php

XServer の場合ですが、PHP に cli 版と cgi 版があり、cli 版で動かそうとしますとスクリプトを置いたディレクトリに移動してから実行しないと稼働しません。cgi 版であればフルパスで指定すれば稼働します。これはスクリプトを public_html の外においた場合の話です。

ということで、これで安心してデータベースに触ることができます。