
💾 Резервное копирование и восстановление базы данных MySQL через PHP
Потеря базы данных означает потерю заказов, пользователей и контента за секунды. Для владельца сайта на WordPress резервное копирование не «желательно», а обязательно. Но ручной вход в phpMyAdmin каждый день отнимает время, и в нужный момент бэкапа просто не оказывается.
PHP-скрипт решает проблему: вы даёте ему доступ к базе, он выгружает структуру и данные в SQL-файл. За пять минут настройки вы получаете повторяемый, программируемый бэкап, который не забудете сделать.
Дальше: готовые скрипты резервного копирования и восстановления MySQL на PHP. Разберём, как они работают, где взять доступы и как запустить авто-бэкап по расписанию, чтобы база данных never оставалась без защиты.
💡 Быстрый обзор:
- Подключаемся к MySQL через
mysqli, получаем список таблиц и выгружаем структуру - Формируем SQL-дамп:
CREATE TABLEплюсINSERTдля каждой таблицы, сохраняем в файл - Восстановление: читаем дамп и выполняем запросы одним пакетом через
mysqli_multi_query - Автоматизация через Cron (Linux) или Task Scheduler (Windows) запускает скрипт без вашего участия
- Сжатие в
.gzи ротация старых копий экономят место и держат историю бэкапов
Шаг 1: Доступы и окружение: что нужно перед запуском
Перед тем как писать код, соберите три вещи: учётные данные MySQL, директорию для хранения бэкапов и понимание, где будет запускаться скрипт.
Для подключения к базе нужны хост (обычно localhost или 127.0.0.1), имя пользователя, пароль и название базы. На WordPress-сайте эти данные лежат в wp-config.php: константы DB_HOST (сервер), DB_USER (логин), DB_PASSWORD (пароль) и DB_NAME (имя базы).
Директорию для бэкапов создайте вне публичной папки сайта. Положите папку backups/ на уровень выше public_html, так к SQL-файлам не будет доступа из браузера. Права на запись (chmod 755 или chmod 775) обязательны: без них fopen() и fwrite() упадут с ошибкой.
PHP-скрипт запускается из командной строки: php backup.php. Веб-запуск (через браузер) тоже возможен, но командная строка безопаснее, никто не подберёт URL скрипта и не сольёт вашу базу.
Шаг 2: PHP-скрипт резервного копирования базы данных
Скрипт делает три прохода: получает список таблиц, для каждой, выгружает структуру (SHOW CREATE TABLE), затем, данные (SELECT *). Результат собирается в одну строку и пишется в .sql-файл. Сохраните код как backup.php и запустите из терминала.
1 <?php 2 3 $connection = mysqli_connect('localhost', 'user', 'pass', 'my_database'); 4 5 if (!$connection) { 6 die('Ошибка подключения: ' . mysqli_connect_error()); 7 } 8 9 $tables = []; 10 $result = mysqli_query($connection, "SHOW TABLES"); 11 while ($row = mysqli_fetch_row($result)) { 12 $tables[] = $row[0]; 13 } 14 15 $dump = ''; 16 foreach ($tables as $table) { 17 $dump .= 'DROP TABLE IF EXISTS ' . $table . ";\n"; 18 19 $create = mysqli_query($connection, "SHOW CREATE TABLE " . $table); 20 $row = mysqli_fetch_row($create); 21 $dump .= $row[1] . ";\n\n"; 22 23 $data = mysqli_query($connection, "SELECT * FROM " . $table); 24 $num_fields = mysqli_num_fields($data); 25 26 while ($row = mysqli_fetch_row($data)) { 27 $dump .= "INSERT INTO " . $table . " VALUES("; 28 for ($j = 0; $j < $num_fields; $j++) { 29 if (isset($row[$j])) { 30 $row[$j] = mysqli_real_escape_string($connection, $row[$j]); 31 $dump .= '"' . $row[$j] . '"'; 32 } else { 33 $dump .= 'NULL'; 34 } 35 if ($j < $num_fields - 1) { 36 $dump .= ','; 37 } 38 } 39 $dump .= ");\n"; 40 } 41 $dump .= "\n\n"; 42 } 43 44 $filename = 'backups/dump_' . date('Y-m-d_H-i-s') . '.sql'; 45 $handle = fopen($filename, 'w+'); 46 fwrite($handle, $dump); 47 fclose($handle); 48 49 echo "Бэкап сохранён: " . $filename . "\n"; 50 51 ?>
Что здесь происходит построчно. mysqli_connect() открывает соединение, если доступа нет, скрипт падает с понятным сообщением, а не продолжает молча. Цикл SHOW TABLES собирает имена таблиц в массив.
Для каждой таблицы SHOW CREATE TABLE возвращает ровно одну строку со вторым полем, полным DDL-запросом. Мы добавляем DROP TABLE IF EXISTS перед ним, чтобы дамп можно было накатить на чистую базу без конфликтов.
SELECT * читает все строки. Функция mysqli_real_escape_string() экранирует кавычки и спецсимволы внутри значений, в отличие от addslashes(), она учитывает кодировку соединения и не пропускает инъекции через многобайтовые последовательности. NULL-значения вставляются как NULL, а не как пустая строка.
Имя файла содержит дату и время запуска, удобно для ротации. Файл открывается в режиме w+ (создать или перезаписать) и получает весь дамп одной операцией fwrite().
Шаг 3: Скрипт восстановления базы данных из SQL-дампа
Обратный скрипт читает .sql-файл и выполняет запросы к базе. На практике дамп часто весит десятки мегабайт, поэтому file_get_contents() и explode по ;, не лучший вариант для продакшена. Но для учебных целей и небольших баз подход рабочий.
Сохраните как restore.php. Перед запуском сделайте полный бэкап текущей базы, восстановление перезаписывает таблицы.
1 <?php 2 3 $connection = mysqli_connect('localhost', 'root', '', 'test'); 4 5 if (!$connection) { 6 die('Ошибка подключения: ' . mysqli_connect_error()); 7 } 8 9 $filename = 'backups/dump_2026-06-14_12-00-00.sql'; 10 11 if (!file_exists($filename)) { 12 die('Файл дампа не найден: ' . $filename); 13 } 14 15 $contents = file_get_contents($filename); 16 17 if (mysqli_multi_query($connection, $contents)) { 18 do { 19 if ($result = mysqli_store_result($connection)) { 20 mysqli_free_result($result); 21 } 22 } while (mysqli_next_result($connection)); 23 echo "База восстановлена из: " . $filename . "\n"; 24 } else { 25 echo "Ошибка восстановления: " . mysqli_error($connection) . "\n"; 26 } 27 28 mysqli_close($connection); 29 30 ?>
Ключевое отличие от построчного mysqli_query(), mysqli_multi_query(). Она принимает строку с несколькими SQL-запросами и выполняет их последовательно за один вызов. Цикл do...while с mysqli_next_result() обязателен: без него последующие запросы не выполнятся, а соединение зависнет.
mysqli_store_result() забирает результат текущего запроса, а mysqli_free_result() освобождает память. Если запрос, INSERT или CREATE TABLE, результат будет false, и это нормально: скрипт просто переходит к следующему.
Обратите внимание: скрипт не делает DROP DATABASE и не пересоздаёт саму базу. Он ждёт, что база test уже существует. Для перестраховки можно первой строкой дампа держать CREATE DATABASE IF NOT EXISTS.
Шаг 4: Автоматизация бэкапа через Cron и сжатие
Ручной запуск работает для разовых задач. Но на боевом сайте бэкап должен делаться без вас, каждую ночь, пока вы спите.
На Linux-сервере добавьте задачу в Cron: crontab -e и строка 0 3 * * * php /path/to/backup.php запускает скрипт каждый день в 3 часа ночи. На Windows, Task Scheduler с действием «запустить программу» php.exe и аргументом-путём к скрипту.
Две доработки, которые превращают учебный скрипт в production-ready:
Сжатие. После записи .sql-файла добавьте exec("gzip " . $filename);, размер дампа уменьшится в 5-10 раз. Для восстановления gunzip распакует файл обратно.
Ротация. Бэкапы за последние 7 дней храните, остальное удаляйте. Простой вариант, в начало скрипта добавьте чистку файлов старше N дней:
1 $retention_days = 7; 2 $backup_dir = 'backups/'; 3 foreach (glob($backup_dir . '*.sql.gz') as $file) { 4 if (filemtime($file) < time() - $retention_days * 86400) { 5 unlink($file); 6 } 7 }
В результате у вас, автономная система: скрипт запускается по расписанию, создаёт сжатый бэкап с датой в имени и чистит старьё. Прямые руки и 20 минут настройки заменяют платный плагин.
На видео выше, полный цикл авто-бэкапа: от конфигурационного файла до проверки сжатого .sql.gz в папке backups/. Подход ровно тот же, что описан в этой статье, с дополнительными проверками ошибок и логированием.
⁉️🤔 Частые вопросы
Безопасно ли хранить пароль MySQL в PHP-скрипте?
Хранить учётные данные в открытом виде внутри скрипта, приемлемо только если файл лежит вне публичной директории сайта и недоступен из браузера. Лучше вынести доступы в отдельный
config.phpза пределамиpublic_htmlи подключать черезrequire. Ещё надёжнее, передавать пароль через переменную окружения:getenv('DB_PASS').
Можно ли бэкапить только определённые таблицы?
Да. Замените
SHOW TABLESна явный массив имён:$tables = ['wp_posts', 'wp_postmeta', 'wp_options'];. Или исключите неважные таблицы черезif (!in_array($table, ['wp_litespeed_cache', 'wp_actionscheduler_logs'])). На практике таблицы кэшей и логов занимают значительную часть размера базы и не нужны для восстановления.
Сколько места занимает бэкап базы среднего WordPress-сайта?
Несжатый SQL-дамп базы на 500 записей и 15 плагинов весит 15-40 МБ. После
gzip, 2-8 МБ. За месяц ежедневных бэкапов со сжатием накапливается 100-300 МБ, для сервера с 20 ГБ диска это незаметно. Ротация за 7 дней держит занятое место в пределах 50-100 МБ.
Что делать, если бэкап-файл повреждён?
Перед восстановлением проверьте целостность:
gunzip -t dump.sql.gzдля сжатых,head -n 20 dump.sqlдля несжатых, убедитесь, что файл начинается с SQL-комментариев, а не с бинарного мусора. Повреждённый дамп не восстанавливайте, вы потеряете данные. Именно поэтому держите минимум 2-3 последних копии.
Чем этот подход отличается от плагинов резервного копирования WordPress?
Плагин вроде UpdraftPlus даёт веб-интерфейс, инкрементальные бэкапы и загрузку в облако «из коробки». PHP-скрипт, это полный контроль над процессом без лишнего кода в админке. Для разработчика, который и так работает с сервером, скрипт быстрее и прозрачнее. Для клиента без доступа к консоли, плагин удобнее.
Ручной скрипт, mysqldump или плагин: что выбрать под вашу задачу
PHP-скрипт резервного копирования даёт главное, понимание того, что именно происходит с вашей базой при бэкапе. Вы не доверяете чёрному ящику, а контролируете каждый запрос.
Если сайт на продакшене и важна скорость, берите mysqldump. Одна команда mysqldump -u user -p db_name | gzip > dump.sql.gz делает то же самое, но в разы быстрее, потому что работает на уровне движка MySQL, а не построчным SELECT. Скрипт PHP, для учебных целей, кастомных сценариев (бэкап не всей базы, а результатов конкретного запроса) и сред, где mysqldump недоступен.
Если сайт на WordPress и вы не хотите лезть в консоль, любой плагин резервного копирования закроет задачу без кода. UpdraftPlus, BackWPup, Duplicator забирают и базу, и файлы сайта.
Начните с ручного скрипта, поймите, как формируется SQL-дамп. Затем настройте Cron и сжатие. Последний шаг, проверка восстановления на тестовом сервере: бэкап без проверки восстановления не считается сделанным. Какой инструмент для бэкапа баз данных используете вы? Напишите в комментариях.



