
💾 Kopia zapasowa i przywracanie bazy danych MySQL przez PHP
Utrata bazy danych oznacza utratę zamówień, użytkowników i treści w ciągu sekund. Dla właściciela strony na WordPress kopia zapasowa nie jest „pożądana", lecz obowiązkowa. Jednak ręczne logowanie do phpMyAdmin każdego dnia zabiera czas, a w krytycznym momencie backupu po prostu nie ma.
Skrypt PHP rozwiązuje problem: dajesz mu dostęp do bazy, a on zrzuca strukturę i dane do pliku SQL. W pięć minut konfiguracji otrzymujesz powtarzalny, programowalny backup, o którym nie zapomnisz.
Dalej: gotowe skrypty do tworzenia kopii zapasowej i odtwarzania MySQL w PHP. Przeanalizujemy, jak działają, skąd wziąć dane dostępowe i jak uruchomić automatyczny backup według harmonogramu, aby baza danych nigdy nie pozostawała bez ochrony.
💡 Szybki przegląd:
- Łączymy się z MySQL przez
mysqli, pobieramy listę tabel i zrzucamy strukturę - Tworzymy zrzut SQL:
CREATE TABLEplusINSERTdla każdej tabeli, zapisujemy do pliku - Odtwarzanie: czytamy zrzut i wykonujemy zapytania jednym pakietem przez
mysqli_multi_query - Automatyzacja przez Cron (Linux) lub Harmonogram zadań (Windows) uruchamia skrypt bez Państwa udziału
- Kompresja do
.gzi rotacja starych kopii oszczędzają miejsce i przechowują historię backupów
Krok 1: Dane dostępowe i środowisko: co jest potrzebne przed uruchomieniem
Zanim zaczną Państwo pisać kod, proszę zebrać trzy rzeczy: dane logowania do MySQL, katalog do przechowywania backupów i wiedzę, gdzie skrypt będzie uruchamiany.
Do połączenia z bazą potrzebny jest host (zazwyczaj localhost lub 127.0.0.1), nazwa użytkownika, hasło i nazwa bazy danych. Na stronie WordPress dane te znajdują się w pliku wp-config.php: stałe DB_HOST (serwer), DB_USER (login), DB_PASSWORD (hasło) i DB_NAME (nazwa bazy).
Katalog na backupy proszę utworzyć poza folderem publicznym strony. Proszę umieścić folder backups/ poziom wyżej niż public_html, dzięki czemu pliki SQL nie będą dostępne z przeglądarki. Prawa do zapisu (chmod 755 lub chmod 775) są obowiązkowe: bez nich fopen() i fwrite() zakończą się błędem.
Skrypt PHP uruchamia się z wiersza poleceń: php backup.php. Uruchomienie przez przeglądarkę też jest możliwe, ale wiersz poleceń jest bezpieczniejszy, nikt nie odgadnie adresu URL skryptu i nie zrzuci Państwa bazy.
Krok 2: Skrypt PHP do tworzenia kopii zapasowej bazy danych
Skrypt wykonuje trzy przebiegi: pobiera listę tabel, dla każdej zrzuca strukturę (SHOW CREATE TABLE), a następnie dane (SELECT *). Wynik jest zbierany w jeden ciąg znaków i zapisywany do pliku .sql. Proszę zapisać kod jako backup.php i uruchomić z terminala.
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 ?>
Co tu się dzieje, linijka po linijce. mysqli_connect() otwiera połączenie, jeśli dostęp jest niemożliwy, skrypt przerywa działanie z czytelnym komunikatem, zamiast kontynuować w milczeniu. Pętla SHOW TABLES zbiera nazwy tabel do tablicy.
Dla każdej tabeli SHOW CREATE TABLE zwraca dokładnie jeden wiersz z drugim polem będącym pełnym zapytaniem DDL. Dodajemy DROP TABLE IF EXISTS przed nim, aby zrzut można było wgrać na czystą bazę bez konfliktów.
SELECT * odczytuje wszystkie wiersze. Funkcja mysqli_real_escape_string() maskuje cudzysłowy i znaki specjalne wewnątrz wartości, w przeciwieństwie do addslashes() uwzględnia kodowanie połączenia i nie przepuszcza iniekcji przez sekwencje wielobajtowe. Wartości NULL są wstawiane jako NULL, a nie jako pusty ciąg.
Nazwa pliku zawiera datę i czas uruchomienia, co jest wygodne przy rotacji. Plik jest otwierany w trybie w+ (utwórz lub nadpisz) i otrzymuje cały zrzut w jednej operacji fwrite().
Krok 3: Skrypt do odtwarzania bazy danych ze zrzutu SQL
Skrypt odwrotny odczytuje plik .sql i wykonuje zapytania do bazy. W praktyce zrzut często waży dziesiątki megabajtów, dlatego file_get_contents() i explode po ; nie jest najlepszą opcją na produkcję. Jednak do celów edukacyjnych i dla małych baz to podejście działa.
Proszę zapisać jako restore.php. Przed uruchomieniem należy wykonać pełny backup bieżącej bazy, odtwarzanie nadpisuje tabele.
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 ?>
Kluczowa różnica w stosunku do sekwencyjnego mysqli_query(): mysqli_multi_query(). Przyjmuje ona ciąg z wieloma zapytaniami SQL i wykonuje je sekwencyjnie w jednym wywołaniu. Pętla do...while z mysqli_next_result() jest obowiązkowa: bez niej kolejne zapytania nie zostaną wykonane, a połączenie się zawiesi.
mysqli_store_result() pobiera wynik bieżącego zapytania, a mysqli_free_result() zwalnia pamięć. Jeśli zapytanie, INSERT lub CREATE TABLE, zwróci wynik false, jest to normalne: skrypt po prostu przechodzi do następnego.
Proszę zwrócić uwagę: skrypt nie wykonuje DROP DATABASE i nie odtwarza samej bazy. Oczekuje, że baza test już istnieje. Dla bezpieczeństwa można w pierwszym wierszu zrzutu umieścić CREATE DATABASE IF NOT EXISTS.
Krok 4: Automatyzacja backupu przez Cron i kompresja
Ręczne uruchamianie sprawdza się przy jednorazowych zadaniach. Jednak na stronie produkcyjnej backup powinien być wykonywany bez Państwa udziału, każdej nocy, kiedy Państwo śpią.
Na serwerze Linux proszę dodać zadanie w Cronie: crontab -e i wiersz 0 3 * * * php /path/to/backup.php uruchamia skrypt codziennie o 3 w nocy. W systemie Windows Harmonogram zadań z akcją „uruchom program" php.exe i argumentem będącym ścieżką do skryptu.
Dwa ulepszenia, które przekształcają skrypt edukacyjny w gotowy do użycia na produkcji:
Kompresja. Po zapisaniu pliku .sql proszę dodać exec("gzip " . $filename);, rozmiar zrzutu zmniejszy się 5-10 razy. Do odtworzenia gunzip rozpakuje plik z powrotem.
Rotacja. Backupy z ostatnich 7 dni proszę przechowywać, resztę usuwać. Prosty wariant: na początku skryptu proszę dodać czyszczenie plików starszych niż N dni:
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 }
W rezultacie mają Państwo autonomiczny system: skrypt uruchamia się zgodnie z harmonogramem, tworzy skompresowany backup z datą w nazwie i czyści stare pliki. Własne umiejętności i 20 minut konfiguracji zastępują płatną wtyczkę.
Na filmie powyżej pełny cykl automatycznego backupu: od pliku konfiguracyjnego do sprawdzenia skompresowanego .sql.gz w folderze backups/. Podejście jest dokładnie takie samo, jak opisano w tym artykule, z dodatkowymi kontrolami błędów i logowaniem.
⁉️🤔 Często zadawane pytania
Czy przechowywanie hasła MySQL w skrypcie PHP jest bezpieczne?
Przechowywanie danych logowania jawnym tekstem wewnątrz skryptu jest dopuszczalne tylko wtedy, gdy plik znajduje się poza publicznym katalogiem strony i jest niedostępny z przeglądarki. Lepiej wynieść dane dostępowe do osobnego pliku
config.phppozapublic_htmli dołączać go przezrequire. Jeszcze bezpieczniej jest przekazywać hasło przez zmienną środowiskową:getenv('DB_PASS').
Czy można zrobić backup tylko wybranych tabel?
Tak. Proszę zastąpić
SHOW TABLESjawną tablicą nazw:$tables = ['wp_posts', 'wp_postmeta', 'wp_options'];. Lub wykluczyć nieistotne tabele przezif (!in_array($table, ['wp_litespeed_cache', 'wp_actionscheduler_logs'])). W praktyce tabele pamięci podręcznej i logów zajmują znaczną część rozmiaru bazy i nie są potrzebne do odtworzenia.
Ile miejsca zajmuje backup bazy przeciętnej strony na WordPress?
Nieskompresowany zrzut SQL bazy z 500 wpisami i 15 wtyczkami waży 15-40 MB. Po
gzip2-8 MB. Przez miesiąc codziennych backupów z kompresją zbiera się 100-300 MB, dla serwera z dyskiem 20 GB jest to niezauważalne. Rotacja za 7 dni utrzymuje zajęte miejsce w granicach 50-100 MB.
Co zrobić, jeśli plik backupu jest uszkodzony?
Przed odtworzeniem proszę sprawdzić integralność:
gunzip -t dump.sql.gzdla skompresowanych,head -n 20 dump.sqldla nieskompresowanych, proszę upewnić się, że plik zaczyna się od komentarzy SQL, a nie od binarnych śmieci. Uszkodzonego zrzutu proszę nie odtwarzać, stracą Państwo dane. Właśnie dlatego proszę przechowywać co najmniej 2-3 ostatnie kopie.
Czym to podejście różni się od wtyczek do tworzenia kopii zapasowych WordPress?
Wtyczka taka jak UpdraftPlus daje interfejs webowy, przyrostowe backupy i wysyłanie do chmury „od ręki". Skrypt PHP to pełna kontrola nad procesem bez zbędnego kodu w panelu administracyjnym. Dla developera, który i tak pracuje z serwerem, skrypt jest szybszy i bardziej przejrzysty. Dla klienta bez dostępu do konsoli wtyczka jest wygodniejsza.
Ręczny skrypt, mysqldump czy wtyczka: co wybrać do swojego zadania
Skrypt PHP do tworzenia kopii zapasowych daje to, co najważniejsze: zrozumienie, co dokładnie dzieje się z Państwa bazą podczas backupu. Nie ufają Państwo czarnej skrzynce, lecz kontrolują każde zapytanie.
Jeśli strona jest na produkcji i ważna jest szybkość, proszę użyć mysqldump. Jedno polecenie mysqldump -u user -p db_name | gzip > dump.sql.gz robi to samo, ale wielokrotnie szybciej, ponieważ działa na poziomie silnika MySQL, a nie sekwencyjnym SELECT. Skrypt PHP jest do celów edukacyjnych, niestandardowych scenariuszy (backup nie całej bazy, lecz wyników konkretnego zapytania) i środowisk, gdzie mysqldump jest niedostępny.
Jeśli strona jest na WordPress i nie chcą Państwo wchodzić do konsoli, dowolna wtyczka do backupu załatwi sprawę bez kodu. UpdraftPlus, BackWPup, Duplicator pobierają zarówno bazę, jak i pliki strony.
Proszę zacząć od ręcznego skryptu, zrozumieć, jak tworzony jest zrzut SQL. Następnie proszę skonfigurować Cron i kompresję. Ostatni krok to sprawdzenie odtwarzania na serwerze testowym: backup bez sprawdzenia odtwarzania nie jest uznawany za wykonany. Jakiego narzędzia do backupu baz danych Państwo używają? Proszę napisać w komentarzach.



