Skip to content

Всё для WordPress, веб-разработки — и не только

💾 Резервное копирование и восстановление базы данных MySQL через PHP

💾 Резервное копирование и восстановление базы данных 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
5if (!$connection) {
6 die('Ошибка подключения: ' . mysqli_connect_error());
7}
8
9$tables = [];
10$result = mysqli_query($connection, "SHOW TABLES");
11while ($row = mysqli_fetch_row($result)) {
12 $tables[] = $row[0];
13}
14
15$dump = '';
16foreach ($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+');
46fwrite($handle, $dump);
47fclose($handle);
48
49echo "Бэкап сохранён: " . $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
5if (!$connection) {
6 die('Ошибка подключения: ' . mysqli_connect_error());
7}
8
9$filename = 'backups/dump_2026-06-14_12-00-00.sql';
10
11if (!file_exists($filename)) {
12 die('Файл дампа не найден: ' . $filename);
13}
14
15$contents = file_get_contents($filename);
16
17if (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
28mysqli_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/';
3foreach (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 и сжатие. Последний шаг, проверка восстановления на тестовом сервере: бэкап без проверки восстановления не считается сделанным. Какой инструмент для бэкапа баз данных используете вы? Напишите в комментариях.