
💾 Copia de seguridad y restauración de una base de datos MySQL mediante PHP
Perder una base de datos significa perder pedidos, usuarios y contenido en segundos. Para el propietario de un sitio WordPress, la copia de seguridad no es «algo deseable», es obligatoria. Pero iniciar sesión manualmente en phpMyAdmin cada día es una pérdida de tiempo y, cuando la necesita, la copia simplemente no está ahí.
Un script PHP resuelve el problema: usted le da acceso a la base de datos y él exporta la estructura y los datos a un archivo SQL. En cinco minutos de configuración obtiene una copia de seguridad repetible y programable que no olvidará hacer.
A continuación: scripts listos para usar de copia de seguridad y restauración de MySQL en PHP. Desglosaremos cómo funcionan, dónde obtener las credenciales y cómo ejecutar la copia automática programada para que su base de datos nunca quede desprotegida.
💡 Resumen rápido:
- Conectar a MySQL mediante
mysqli, obtener la lista de tablas y exportar la estructura - Formar el volcado SQL:
CREATE TABLEmásINSERTpara cada tabla, guardar en archivo - Restaurar: leer el volcado y ejecutar las consultas en lote mediante
mysqli_multi_query - Automatización mediante Cron (Linux) o el Programador de tareas (Windows) ejecuta el script sin su intervención
- La compresión a
.gzy la rotación de copias antiguas ahorran espacio y mantienen el historial de copias
Paso 1: Credenciales y entorno, lo que necesita antes de ejecutar
Antes de escribir código, reúna tres cosas: las credenciales de MySQL, un directorio para almacenar las copias y saber dónde se ejecutará el script.
Para conectarse a la base de datos necesita el host (normalmente localhost o 127.0.0.1), el nombre de usuario, la contraseña y el nombre de la base de datos. En un sitio WordPress estos datos residen en wp-config.php: las constantes DB_HOST (servidor), DB_USER (usuario), DB_PASSWORD (contraseña) y DB_NAME (nombre de la base de datos).
Cree el directorio de copias fuera de la carpeta pública del sitio. Coloque la carpeta backups/ un nivel por encima de public_html, para que los archivos SQL no sean accesibles desde el navegador. Los permisos de escritura (chmod 755 o chmod 775) son obligatorios: sin ellos, fopen() y fwrite() fallarán con un error.
El script PHP se ejecuta desde la línea de comandos: php backup.php. La ejecución web (a través del navegador) también es posible, pero la línea de comandos es más segura, nadie adivinará la URL del script y volcará su base de datos.
Paso 2: Script PHP para la copia de seguridad de la base de datos
El script realiza tres pasadas: obtiene la lista de tablas, para cada una exporta la estructura (SHOW CREATE TABLE) y luego los datos (SELECT *). El resultado se ensambla en una cadena y se escribe en un archivo .sql. Guarde el código como backup.php y ejecútelo desde la terminal.
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 ?>
Qué sucede aquí línea por línea. mysqli_connect() abre una conexión; si se deniega el acceso, el script finaliza con un mensaje claro en lugar de continuar silenciosamente. El bucle SHOW TABLES recopila los nombres de las tablas en un array.
Para cada tabla, SHOW CREATE TABLE devuelve exactamente una fila cuyo segundo campo es la consulta DDL completa. Añadimos DROP TABLE IF EXISTS antes para que el volcado pueda aplicarse a una base de datos limpia sin conflictos.
SELECT * lee todas las filas. La función mysqli_real_escape_string() escapa las comillas y los caracteres especiales dentro de los valores; a diferencia de addslashes(), respeta la codificación de la conexión y no omite inyecciones mediante secuencias multibyte. Los valores NULL se insertan como NULL, no como cadena vacía.
El nombre del archivo contiene la fecha y hora de ejecución, lo cual es conveniente para la rotación. El archivo se abre en modo w+ (crear o sobrescribir) y recibe el volcado completo en una sola operación fwrite().
Paso 3: Script para restaurar la base de datos desde un volcado SQL
El script inverso lee el archivo .sql y ejecuta las consultas contra la base de datos. En la práctica, los volcados suelen pesar decenas de megabytes, por lo que file_get_contents() y dividir por ; no es la mejor opción para producción. Pero para fines de aprendizaje y bases de datos pequeñas, el enfoque funciona.
Guárdelo como restore.php. Antes de ejecutarlo, haga una copia de seguridad completa de la base de datos actual; la restauración sobrescribe las tablas.
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 ?>
La diferencia clave respecto a mysqli_query() línea por línea es mysqli_multi_query(). Esta acepta una cadena con múltiples consultas SQL y las ejecuta secuencialmente en una sola llamada. El bucle do...while con mysqli_next_result() es obligatorio: sin él, las consultas posteriores no se ejecutarán y la conexión se bloqueará.
mysqli_store_result() obtiene el resultado de la consulta actual y mysqli_free_result() libera memoria. Si la consulta es INSERT o CREATE TABLE, el resultado será false, y eso es normal: el script simplemente pasa a la siguiente.
Nota: el script no ejecuta DROP DATABASE ni recrea la base de datos en sí. Espera que la base de datos test ya exista. Por seguridad, la primera línea del volcado puede contener CREATE DATABASE IF NOT EXISTS.
Paso 4: Automatización de la copia mediante Cron y compresión
La ejecución manual funciona para tareas puntuales. Pero en un sitio en producción, la copia de seguridad debe realizarse sin usted, cada noche mientras duerme.
En un servidor Linux, añada una tarea a Cron: crontab -e y la línea 0 3 * * * php /path/to/backup.php ejecuta el script cada día a las 3 AM. En Windows, el Programador de tareas con la acción «iniciar un programa» php.exe y la ruta del script como argumento.
Dos mejoras que convierten un script de aprendizaje en uno listo para producción:
Compresión. Después de escribir el archivo .sql, añada exec("gzip " . $filename);; el tamaño del volcado se reducirá de 5 a 10 veces. Para restaurar, gunzip descomprime el archivo de nuevo.
Rotación. Conserve las copias de los últimos 7 días y elimine el resto. Una opción simple: añada al inicio del script una limpieza de archivos con más de N días de antigüedad:
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 }
Como resultado, tiene un sistema autónomo: el script se ejecuta según lo programado, crea una copia comprimida con la fecha en el nombre del archivo y limpia el material antiguo. Con buenas manos y 20 minutos de configuración, sustituye a un plugin de pago.
El video anterior muestra el ciclo completo de copia de seguridad automática: desde el archivo de configuración hasta la verificación del .sql.gz comprimido en la carpeta backups/. El enfoque es exactamente el mismo que se describe en este artículo, con comprobaciones de errores y registro adicionales.
⁉️🤔 FAQ
¿Es seguro almacenar la contraseña de MySQL en un script PHP?
Almacenar credenciales en texto plano dentro de un script solo es aceptable si el archivo reside fuera del directorio público del sitio y es inaccesible desde el navegador. Es mejor mover las credenciales a un
config.phpseparado fuera depublic_htmle incluirlo medianterequire. Aún más fiable: pase la contraseña mediante una variable de entorno:getenv('DB_PASS').
¿Puedo hacer una copia de seguridad solo de tablas específicas?
Sí. Sustituya
SHOW TABLESpor un array explícito de nombres:$tables = ['wp_posts', 'wp_postmeta', 'wp_options'];. O excluya las tablas no importantes medianteif (!in_array($table, ['wp_litespeed_cache', 'wp_actionscheduler_logs'])). En la práctica, las tablas de caché y registros ocupan una parte significativa del tamaño de la base de datos y no son necesarias para la restauración.
¿Cuánto espacio ocupa una copia de seguridad de una base de datos de un sitio WordPress medio?
Un volcado SQL sin comprimir de una base de datos con 500 entradas y 15 plugins pesa entre 15 y 40 MB. Después de
gzip, entre 2 y 8 MB. Un mes de copias diarias con compresión acumula entre 100 y 300 MB; para un servidor con 20 GB de disco, esto es insignificante. La rotación de 7 días mantiene el espacio ocupado entre 50 y 100 MB.
¿Qué hacer si el archivo de copia de seguridad está dañado?
Antes de restaurar, verifique la integridad:
gunzip -t dump.sql.gzpara comprimidos,head -n 20 dump.sqlpara sin comprimir; asegúrese de que el archivo comience con comentarios SQL, no con basura binaria. No restaure un volcado dañado, perderá datos. Esa es exactamente la razón por la que conserva al menos las 2 o 3 últimas copias.
¿En qué se diferencia este enfoque de los plugins de copia de seguridad para WordPress?
Un plugin como UpdraftPlus ofrece una interfaz web, copias incrementales y subida a la nube «lista para usar». Un script PHP supone un control total sobre el proceso sin código adicional en el administrador. Para un desarrollador que ya trabaja con el servidor, el script es más rápido y transparente. Para un cliente sin acceso a la consola, un plugin es más conveniente.
Script manual, mysqldump o plugin: qué elegir para su tarea
Un script PHP de copia de seguridad le aporta lo principal: comprender exactamente qué le sucede a su base de datos durante la copia. No confía en una caja negra, controla cada consulta.
Si el sitio está en producción y la velocidad importa, use mysqldump. Un solo comando mysqldump -u user -p db_name | gzip > dump.sql.gz hace lo mismo pero mucho más rápido, porque trabaja a nivel del motor MySQL, no con SELECT fila por fila. Un script PHP es para fines de aprendizaje, escenarios personalizados (respaldar no toda la base de datos, sino los resultados de una consulta específica) y entornos donde mysqldump no está disponible.
Si el sitio está en WordPress y no quiere profundizar en la consola, cualquier plugin de copias de seguridad resolverá la tarea sin código. UpdraftPlus, BackWPup, Duplicator capturan tanto la base de datos como los archivos del sitio.
Comience con un script manual, comprenda cómo se forma un volcado SQL. Luego configure Cron y la compresión. Como último paso, verifique la restauración en un servidor de pruebas: una copia de seguridad sin verificación de restauración no se considera completa. ¿Qué herramienta utiliza para la copia de seguridad de la base de datos? Escríbalo en los comentarios.



