Администрирование сайта. Backup базы данных с помощью PHP скрипта и ограничения хостинга

Владимир | | PHP, Разное.

Логотип для backup базы данных
Недавно я столкнулся с небольшой проблемой. Нужно было организовать автоматическое создание резервных копий базы данных.

Задача, конечно, стандартная, а, учитывая объем базы (меньше 1МБ), может быть решена без специального софта и оборудования.

Так что я выбрал самый простой способ (осуществляется в три этапа):

1) создание дампа БД;
2) упаковка дампа в архив;
3) отправка архива на eMail.

На сегодняшний день, объемы почтовых ящиков позволяют вообще не удалять эти архивы, и надежность решения получается довольно высокой, т.к. данные хранятся на разных серверах.

Т.е. все, что нужно сделать – написать скрипт и запускать его с помощью cron.

Скрипт я решил сделать на PHP, и сразу же столкнулся с проблемой.

Для создания дампа базы удобнее всего использовать утилиту mysqldump, которая у меня на хостинге прекрасно работет из консоли, но не запускается из скрипта.

Причина оказалась очень простой. В настройках PHP на сервере хостера были отключены функции: shell_exec(), exec() и т.п.

Поэтому пришлось делать backup в два этапа.

1) Создание дампа БД
Для этого планировщик я просто добавил команду:
mysqldump -uuser_name -hhost_name -puser_password db_name > /path_to_tmp/db_backup/mydbdump.sql

В результате выполнения этой команды, создается файл mydbdump.sql с дампом базы, который будет размещен в папке /path_to_tmp/db_backup/. В параметрах команды, естественно, нужно указать настоящие имена базы, пользователя, пароль и путь к папке /tmp.

Примечание. Для папки /path_to_tmp/db_backup/ должны быть установлены права на запись.

2) Упаковка дампа в архив и отправка по eMail

Эти операции выполняются одним скриптом.

<?php
/**
 * Эту функцию я взял на сайте
 * http://www.weberdev.com/get_example-4173.html
 */
function mail_attachment($filename, $path, $mailto, $from_mail, $from_name, $replyto, $subject, $message) {
    $file = $path.$filename;
    $file_size = filesize($file);
    $handle = fopen($file, "r");
    $content = fread($handle, $file_size);
    fclose($handle);
    $content = chunk_split(base64_encode($content));
    $uid = md5(uniqid(time()));
    $name = basename($file);
    $header = "From: ".$from_name." <".$from_mail.">\r\n";
    $header .= "Reply-To: ".$replyto."\r\n";
    $header .= "MIME-Version: 1.0\r\n";
    $header .= "Content-Type: multipart/mixed; boundary=\"".$uid."\"\r\n\r\n";
    $header .= "This is a multi-part message in MIME format.\r\n";
    $header .= "--".$uid."\r\n";
    $header .= "Content-type:text/plain; charset=iso-8859-1\r\n";
    $header .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
    $header .= $message."\r\n\r\n";
    $header .= "--".$uid."\r\n";
    $header .= "Content-Type: application/octet-stream; name=\"".$filename."\"\r\n"; // use diff. tyoes here
    $header .= "Content-Transfer-Encoding: base64\r\n";
    $header .= "Content-Disposition: attachment; filename=\"".$filename."\"\r\n\r\n";
    $header .= $content."\r\n\r\n";
    $header .= "--".$uid."--";
    if (mail($mailto, $subject, "", $header)) {
//        echo "mail send ... OK"; // or use booleans here
    } else {
        echo "mail send ... ERROR!";
    }
}

//размещение и имя файла с дампом базы
$fileName = '/path_to_tmp/db_backup/mydbdump.sql';
//размещение архива с дампом
$archivePath = '/home/simpleco/tmp/db_backup/';
//имя архива
$archiveName = 'rssevents_backup_'.date('Y_m_j_G_i').'.zip';

//создаем архив $zip = new ZipArchive(); if ($zip->open($archivePath.$archiveName, ZIPARCHIVE::CREATE) === TRUE) { //добавляем в архив файл с дампом $zip->addFile($fileName, 'mydbdump.sql'); $zip->close(); // echo 'ok'; } else { echo 'failed creating zip'; } //устанавливаем переменные для отправки почты //имя и путь к файлу с архивом $my_file = $archiveName; $my_path = $archivePath; //имя отправителя $my_name = "my_name"; //обратный адрес $my_mail = "admin@simplecoding.org"; $my_replyto = "admin@simplecoding.org"; //тема письма $my_subject = "RSS events backup"; //текст письма $my_message = 'Во вложении находится дамп базы. '.date('F-j-Y G:i'); //отправляем письмо (в третьем параметре адрес получателя) mail_attachment($my_file, $my_path, "account_name@gmail.com", $my_mail, $my_name, $my_replyto, $my_subject, $my_message); @unlink($archivePath.$archiveName); @unlink($fileName); ?>

Разберем, как работает этот скрипт.

В начале (строки 6 — 35) объявлена функция mail_attachment, которая выполняет отправку письма с вложением. Признаюсь честно, писать эту функцию самому мне было лень 🙂 . Поэтому я использовал готовую – с этого сайта.

После этого, упаковываем файл с дампом в архив (строки 45 — 53). Для этого создаем объект типа ZipArchive и вызываем его метод open с параметром ZIPARCHIVE::CREATE. А с помощью метода addFile добавляем в архив файл с дампом.
И, естественно, не забываем закрыть архив (метод close).

Примечание. Подробнее почитать о создании архивов с помощью PHP можно в статье «Создание zip архивов на PHP».

Дамп упакован, добавляем его в качестве вложения к письму и отправляем (строка 70). Имена параметров функции mail_attachment говорят сами за себя, поэтому описывать я их не буду.

Завершающим этапом, удаляем файлы с дампом и архивом (строки 72, 73).

Теперь осталось только загрузить этот скрипт на сервер и добавить команду для cron.

php -q /path_to_backup_script/dbbackup.php

Время и периодичность запуска скриптов особой роли не играет. Но, естественно, скрипт отправки письма должен выполняться после того, как будет создан дамп.

Как видите, решение получилось не очень красивое, но работоспособное. Конечно, использовать его для больших баз данных не получится. Но, с другой стороны, если ваша база содержит десятки или сотни мегабайт информации, то вряд ли она находится на shared хостинге 🙂 .

Удачи!

А как вы делаете бэкапы?

  • Хороший хостинг, например, позволяет делать регулярные бэкапы всего что есть. И даже позволяет настроить автоматическую заливку на внешний FTP.

    А так, в свое время я писал скрипт для бэкапа. Который делал полный и инкрементальный бэкапы, формировал GZ-архивы и отправлял на мыло (или заливал на указанный FTP).

    Код для базы был выдран из PhpMyAdmin и немного переписан. (а не использовался внешний mysqldump).
    Работало достаточно быстро, и не требовало возможности запускать файлы (т.к. архивы тоже формировались при помощи PHP).

    Но с наличием нормальной системы бэкапа это совершенно не нужно 🙂

    • >> с наличием нормальной системы бэкапа это совершенно не нужно

      Согласен, только она не всегда нормальная 🙂

  • Хороший хостинг, например, позволяет делать регулярные бэкапы всего что есть. И даже позволяет настроить автоматическую заливку на внешний FTP.

    А так, в свое время я писал скрипт для бэкапа. Который делал полный и инкрементальный бэкапы, формировал GZ-архивы и отправлял на мыло (или заливал на указанный FTP).

    Код для базы был выдран из PhpMyAdmin и немного переписан. (а не использовался внешний mysqldump).
    Работало достаточно быстро, и не требовало возможности запускать файлы (т.к. архивы тоже формировались при помощи PHP).

    Но с наличием нормальной системы бэкапа это совершенно не нужно 🙂

    • >> с наличием нормальной системы бэкапа это совершенно не нужно

      Согласен, только она не всегда нормальная 🙂

  • Eagle

    А я делаю так:

    http://sypex.net/
    Sypex Dumper

    +

    CRON

    • Спасибо за ссылку.
      Интересный вариант, нужно будет попробовать.

      • Sam

        Вариант, кстати, очень даже. Я пользовал его для дампов в 200 и более мб.

  • Eagle

    А я делаю так:

    http://sypex.net/
    Sypex Dumper

    +

    CRON

    • Спасибо за ссылку.
      Интересный вариант, нужно будет попробовать.

      • Sam

        Вариант, кстати, очень даже. Я пользовал его для дампов в 200 и более мб.

  • Зачем создавать ZIP архив в PHP, когда можно использовать Linux/Unix'овый gzip или compress.zlib:// ?

  • Зачем создавать ZIP архив в PHP, когда можно использовать Linux/Unix'овый gzip или compress.zlib:// ?

  • Sam

    Так не попрёт?

    mailx -s «Recent Backup» me@test.com < mysqldump –add-drop-database –add-drop-table –user username –
    password=password1 –databases db1 db2

    • Попрет 🙂
      Если, конечно, есть доступ к mailx

      • Sam

        Ну, к какому-то мейлеру точно должен быть доступ… не обязательно к mailx.

  • Sam

    Так не попрёт?

    mailx -s «Recent Backup» me@test.com < mysqldump –add-drop-database –add-drop-table –user username –
    password=password1 –databases db1 db2

    • Попрет 🙂
      Если, конечно, есть доступ к mailx

      • Sam

        Ну, к какому-то мейлеру точно должен быть доступ… не обязательно к mailx.

  • Sam, хороший способ, спасибо, еще бы жать это счастье с помощью GZIP, но с этим уже сам смогу разобраться.

  • Sam, хороший способ, спасибо, еще бы жать это счастье с помощью GZIP, но с этим уже сам смогу разобраться.

  • Анатолий

    У меня письмо приходит, но почему-то пустое (без прикреплённого дампа), даже текста «Во вложении находится дамп базы.» нет. А вес письма указывается. Помогите пожалуйста.

    • Проверьте:
      1) создан ли файл mydbdump.sql;
      2) сверьте путь к mydbdump.sql (строка 38) и его реальное размещение;
      3) убедитесь, что для папку с архивом (строка 40) есть доступ на запись и архив создаётся.

      Дальнейшие действия зависят от результатов проверки.

      • Анатолий

        Владимир, спасибо что ответили. Итак,
        1. Файл .sql создаётся.
        2. mydbdump.sql создаётся там, где и должен. (соответсвует строке 38.
        3. .zip файл создаётся там же, где и .sql (путь у них одинаковый). Доступ на запись есть.

        Я так думаю, что файл не может прикрепиться. Но тогда почему письмо приходит с весом архива, а самого архива в нём нет.

      • Ещё есть такие догадки:
        В 6 строчке указаны параметры отличные от тех, что в 70 строке. Может быть в строках 6-35 изменить параметры добавив my_?
        P.S. Думаю Вы поняли о чём я.

        • Нет, переименовывать параметры не обязательно. Внутри функции они доступны с теми именами, которые объявлены в перечне параметров. Чтобы убедиться можете вывести значение любого из них с помощью echo.

          Попробуйте отправить письмо на другой почтовый ящик, может быть проблема в том, что некорректно распознаются заголовки письма.

          Ещё один вариант — использовать класс phpmailer или аналогичный, у них гораздо больше возможностей чем у этой функции.

        • Владимир, спасибо, но я всё-же не смог разобраться с Вашим скриптиком, и поэтому решил сделать всё через cron:
          Добавить в список задач cron следующие задачи:
          1. mysqldump -uuser -hlocalhost -ppass DBname > /путь_к_файлу_в_котором_будет_дамп/dbases.sql
          Данная задача отвечает за создание дампа базы данных.
          2. gzip -c /путь_к_файлу_в_котором_будет_дамп/dbases.sql | uuencode dbases.gz | mail -s Тема_письма mymail@mysait.com
          Эта задача отвечает за упаковку дампа в архив и отправку его на мыло.
          3. Создать php файл со следующим содержанием:

          И закачиваем его в папку на хостинг
          Далее делаем задание cron на выполнение этого файла:
          php -q /путь_к_файлу_который_только_что_создали/del.php
          Данный файл и команда будет удалять созданную базу данных.
          Настроить выполнение данных задач следует по-порядку, через каждую минуту (ну или час, это уже не принципиально).
          P.S. Если сделал не совсем красиво или не правильно, пожалуйста скажите что. Не знаю какую нагрузку будут создавать данные задачи, поэтому, может их можно оптимизировать?

        • Нет, вы все делаете правильно 😉 Если есть возможность запустить gzip из консоли, то, скорее всего, такой вариант будет работать быстрее чем архивация с помощью библиотек php. Кстати, nix системы позволяют объединять команды в консоли, т.е. можно сделать дамп базы и сжать его одной командой. Сходу я ее не напишу, но вы можете поискать тьюториал на эту тему, их довольно много.

  • Анатолий

    У меня письмо приходит, но почему-то пустое (без прикреплённого дампа), даже текста «Во вложении находится дамп базы.» нет. А вес письма указывается. Помогите пожалуйста.

    • Проверьте:
      1) создан ли файл mydbdump.sql;
      2) сверьте путь к mydbdump.sql (строка 38) и его реальное размещение;
      3) убедитесь, что для папку с архивом (строка 40) есть доступ на запись и архив создаётся.

      Дальнейшие действия зависят от результатов проверки.

      • Анатолий

        Владимир, спасибо что ответили. Итак,
        1. Файл .sql создаётся.
        2. mydbdump.sql создаётся там, где и должен. (соответсвует строке 38.
        3. .zip файл создаётся там же, где и .sql (путь у них одинаковый). Доступ на запись есть.

        Я так думаю, что файл не может прикрепиться. Но тогда почему письмо приходит с весом архива, а самого архива в нём нет.

      • Ещё есть такие догадки:
        В 6 строчке указаны параметры отличные от тех, что в 70 строке. Может быть в строках 6-35 изменить параметры добавив my_?
        P.S. Думаю Вы поняли о чём я.

        • Нет, переименовывать параметры не обязательно. Внутри функции они доступны с теми именами, которые объявлены в перечне параметров. Чтобы убедиться можете вывести значение любого из них с помощью echo.

          Попробуйте отправить письмо на другой почтовый ящик, может быть проблема в том, что некорректно распознаются заголовки письма.

          Ещё один вариант — использовать класс phpmailer или аналогичный, у них гораздо больше возможностей чем у этой функции.

        • Владимир, спасибо, но я всё-же не смог разобраться с Вашим скриптиком, и поэтому решил сделать всё через cron:
          Добавить в список задач cron следующие задачи:
          1. mysqldump -uuser -hlocalhost -ppass DBname > /путь_к_файлу_в_котором_будет_дамп/dbases.sql
          Данная задача отвечает за создание дампа базы данных.
          2. gzip -c /путь_к_файлу_в_котором_будет_дамп/dbases.sql | uuencode dbases.gz | mail -s Тема_письма mymail@mysait.com
          Эта задача отвечает за упаковку дампа в архив и отправку его на мыло.
          3. Создать php файл со следующим содержанием:

          И закачиваем его в папку на хостинг
          Далее делаем задание cron на выполнение этого файла:
          php -q /путь_к_файлу_который_только_что_создали/del.php
          Данный файл и команда будет удалять созданную базу данных.
          Настроить выполнение данных задач следует по-порядку, через каждую минуту (ну или час, это уже не принципиально).
          P.S. Если сделал не совсем красиво или не правильно, пожалуйста скажите что. Не знаю какую нагрузку будут создавать данные задачи, поэтому, может их можно оптимизировать?

        • Нет, вы все делаете правильно 😉 Если есть возможность запустить gzip из консоли, то, скорее всего, такой вариант будет работать быстрее чем архивация с помощью библиотек php. Кстати, nix системы позволяют объединять команды в консоли, т.е. можно сделать дамп базы и сжать его одной командой. Сходу я ее не напишу, но вы можете поискать тьюториал на эту тему, их довольно много.