Tema 16: Respaldos con mysqldump y pg_dump — la red de seguridad del DBA
#PF-016 | XP: 280 | Insignia: Guardiana del Backup | Tier: Senior | Tiempo: 4 h

Objetivo
Al terminar este tema vas a poder ejecutar respaldos lógicos consistentes en MySQL y PostgreSQL, automatizarlos con cron/Task Scheduler, verificar que el respaldo es restaurable, y diseñar una política de retención 3-2-1. También vas a entender por qué un respaldo que nunca se ha restaurado no es un respaldo, es una hipótesis.
Mapa del tema
Visión mexica — Por Citlalli
En el altépetl mexica existían tres copias del tributo mayor: una en el calmécac (archivo del templo), otra en el tecpan (palacio del tlatoani) y una tercera que viajaba con el calpixqui cuando partía a las provincias conquistadas. Si una se quemaba, las otras dos permitían reconstruir el registro. Esa es exactamente la regla 3-2-1: tres copias, en dos medios diferentes, con una fuera del sitio.
Cuando un DBA me dice "sí, tengo respaldos" pero nunca los ha restaurado en un entorno de prueba, le respondo lo mismo que le diría a un calpixqui que nunca abrió los tributos guardados: no sabes si están bien hasta que los abres. En este tema aprenderás a hacer dumps verificables, no dumps decorativos.
Regla de oro mexica: el respaldo que no se prueba es fe, no ingeniería.
Bloque 1 — La regla 3-2-1 y por qué "tengo un dump" no basta
El error más caro de un DBA junior es confundir tener un archivo de respaldo con tener un sistema de recuperación. Hay tres problemas clásicos:
- 3 copias de los datos (la original + al menos 2 respaldos).
- 2 medios diferentes (disco local + cinta, disco + nube, etc.).
- 1 copia fuera del sitio (otra ciudad, otro proveedor cloud, otra bóveda física).
- 1 copia viva: la base de datos en producción.
- 1 respaldo local: disco USB o NAS en la misma oficina.
- 1 respaldo remoto: S3, Google Cloud Storage, Backblaze B2 o similar.
Bloque 2 — `mysqldump` para MySQL: opciones que importan
`mysqldump` produce SQL plano (sentencias `CREATE TABLE` + `INSERT`). Útil para bases pequeñas y medianas (hasta ~50 GB con paciencia). Para bases grandes es mejor `mysqlbackup` (Mysql Enterprise) o `mariabackup` (MariaDB / MySQL 8 con binlogs).
Sintaxis mínima:
mysqldump -u root -p tienda_tlalli > backup_tienda_2026-08-23.sqlOpciones que necesitas dominar:
| Opción | Para qué sirve | Cuándo usarla | |---|---|---| | `--single-transaction` | Genera un dump consistente sin bloquear InnoDB | Siempre en producción con InnoDB | | `--routines` | Incluye procedimientos y funciones almacenadas | Cuando tienes SP/Functions | | `--triggers` | Incluye triggers (sí por defecto, pero documenta) | Siempre | | `--events` | Incluye eventos del event scheduler | Si los usas | | `--add-drop-table` | Agrega DROP TABLE antes de CREATE | Para restauraciones limpias | | `--hex-blob` | Exporta BLOB/TEXT en hex | Para campos binarios con caracteres especiales | | `--default-character-set=utf8mb4` | Fuerza encoding de la salida | Crítico en Windows/XAMPP (latin1 por defecto) | | `--column-statistics=0` | Desactiva estadísticas (compatible con MySQL viejo) | Solo si importas a MySQL 5.x |
Ejemplo para producción (tienda Tlalli, ~50 MB):
mysqldump \
-u backup_user \
-p"$(cat ~/.mysql_backup_pwd)" \
--single-transaction \
--routines --triggers --events \
--default-character-set=utf8mb4 \
--hex-blob \
tienda_tlalli | gzip > tienda_2026-08-23.sql.gzDetalle crítico en XAMPP/Windows: el cliente `mysql.exe` y `mysqldump.exe` de XAMPP usan cp1252 por defecto, no UTF-8. Si haces un dump sin `--default-character-set=utf8mb4`, los acentos del nombre "Xochitl Hernández Ruiz" llegan rotos. Siempre agrega la flag.
Para verificar el contenido sin restaurar (revisión rápida):
gunzip -c tienda_2026-08-23.sql.gz | grep -i "INSERT INTO clientes" | head -5Bloque 3 — `pg_dump` para PostgreSQL: tres formatos para tres casos
PostgreSQL ofrece tres formatos de respaldo con `pg_dump`, cada uno con trade-offs distintos:
Formato `plain` (por defecto)
Genera un archivo `.sql` con `CREATE TABLE` + `COPY ... FROM stdin`.
pg_dump -U postgres -h localhost -d tienda_tlalli -F p -f backup_tienda.sql- Pro: legible, editable, se puede inspeccionar con `less`.
- Contra: muy lento para restaurar tablas grandes (ejecuta un `INSERT` por fila).
Formato `custom` (recomendado)
Archivo binario comprimido, restaurable solo con `pg_restore`.
pg_dump -U postgres -h localhost -d tienda_tlalli -F c -f backup_tienda.dump- Pro: comprimido, permite restaurar tablas individuales (`pg_restore --table=clientes`), paralelizable.
- Contra: no es legible en texto plano.
Formato `directory`
Genera un directorio con un archivo por tabla, paralelizable.
pg_dump -U postgres -h localhost -d tienda_tlalli -F d -j 4 -f backup_dir/- Pro: el más rápido para bases grandes (TB), usa múltiples workers.
- Contra: requiere directorio, no un solo archivo.
| Caso de uso | Formato | Comando de respaldo | Comando de restauración | |---|---|---|---| | Inspección manual / debug | `plain` | `pg_dump -F p ...` | `psql -f backup.sql` | | Respaldo diario producción | `custom` | `pg_dump -F c ...` | `pg_restore -d tienda backup.dump` | | Respaldo paralelo (TB) | `directory` | `pg_dump -F d -j 8 ...` | `pg_restore -j 8 ...` | | Migración entre versiones | `plain` | `pg_dump -F p ...` | `psql -f backup.sql` |
Para `tienda_tlalli` (110 pedidos, ~5 MB), el formato `custom` es el balance ideal.
Bloque 4 — Automatización: cron en Linux, Task Scheduler en Windows
Un respaldo que depende de que te acuerdes de hacerlo está roto. Hay que automatizarlo.
Linux/macOS con cron
Edita tu crontab:
crontab -eAgrega:
# Respaldo diario de MySQL a las 2 AM, conservando 7 días
0 2 /opt/backups/scripts/backup_mysql.sh >> /var/log/backup_mysql.log 2>&1Respaldo diario de PostgreSQL a las 3 AM
0 3 /opt/backups/scripts/backup_postgres.sh >> /var/log/backup_postgres.log 2>&1El script `/opt/backups/scripts/backup_mysql.sh`:
#!/usr/bin/env bash
set -euo pipefail
FECHA=$(date +%Y-%m-%d_%H%M)
DEST=/opt/backups/mysql
mysqldump -u backup_user -p"${MYSQL_PWD}" \
--single-transaction --routines --triggers --events \
--default-character-set=utf8mb4 \
tienda_tlalli | gzip > "$DEST/tienda_${FECHA}.sql.gz"
sha256sum "$DEST/tienda_${FECHA}.sql.gz" > "$DEST/tienda_${FECHA}.sha256"
Borrar respaldos de más de 7 días
find "$DEST" -name "tienda_.sql.gz" -mtime +7 -deleteDetalle de seguridad: la contraseña va en una variable de entorno (`MYSQL_PWD`) o en un archivo `.my.cnf` con permisos `chmod 600`. Nunca en la línea de comandos visible en `ps`.
Windows con Task Scheduler
PowerShell script `C:\backups\backup_mysql.ps1`:
$fecha = Get-Date -Format "yyyy-MM-dd_HHmm"
$dest = "C:\backups\mysql"
$cn = "mysql"
& "C:\xampp8\mysql\bin\mysqldump.exe" `
-u backup_user --default-character-set=utf8mb4 `
--single-transaction --routines --triggers --events `
tienda_tlalli | Compress-Archive -DestinationPath "$dest\tienda_$fecha.sql.zip" -Force
Get-FileHash "$dest\tienda_$fecha.sql.zip" -Algorithm SHA256 |
Export-Csv "$dest\tienda_$fecha.sha256.csv" -NoTypeInformation
Get-ChildItem "$dest\tienda_.sql.zip" |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } |
Remove-ItemPrograma la tarea con `schtasks /Create /SC DAILY /TN "BackupMySQL" /TR "powershell -File C:\backups\backup_mysql.ps1" /ST 02:00`.
Bloque 5 — Cifrado y verificación con SHA-256
Un respaldo sin cifrado que sale de tu servidor es un regalo para el primero que lo intercepta. Si el archivo contiene datos personales de clientes (nombre, email, teléfono), las implicaciones legales en México con la LFPDPPP son serias.
Opciones para cifrar el archivo:
- gpg (GnuPG): estándar de oro, gratis, multiplataforma.
gpg --symmetric --cipher-algo AES256 backup_tienda.sql.gz
Te pide passphrase, genera backup_tienda.sql.gz.gpg
- openssl:
openssl enc -aes-256-cbc -salt -in backup_tienda.sql.gz -out backup.tienda.sql.gz.encY la verificación: el SHA-256 del archivo original debe coincidir con el SHA-256 después de la restauración. Si no coinciden, el archivo está corrupto. En el script ya se genera con `sha256sum`. Para verificar después de restaurar:
# Restaurar en directorio temporal
zcat backup_tienda.sql.gz | mysql -u root -p test_restore
mysqldump -u root -p --single-transaction test_restore | sha256sum
Compara con el hash guardado
Si los hashes no coinciden: el respaldo está corrupto. Investiga disco, memoria, canal de transferencia.
AI Mission
Pídele a tu IA copiloto (Claude, ChatGPT, Gemini) que audite tu política de respaldos. Pega este prompt y adapta la respuesta:
Actúa como un DBA senior revisando la política de respaldos de un junior.
Aquí está mi contexto:
- Bases: tienda_tlalli en MySQL 8 (5 MB, InnoDB) y la misma en PostgreSQL 16
- Frecuencia actual de respaldos: manual, cuando me acuerdo
- Almacenamiento: solo en la misma laptop
- Restauración probada por última vez: nunca
Dame:
Los 5 huecos más críticos de mi política.
Un script de bash para Linux que haga respaldo diario automatizado
con verificación SHA-256 y rotación de 7 días.
Una checklist mensual de 4 pasos para probar la restauración.
Compara la respuesta con lo que aprendiste en este tema. Si la IA recomienda algo contradictorio (por ejemplo, "no necesitas SHA-256, MySQL ya lo valida"), decide con criterio propio: MySQL no verifica la integridad del archivo de respaldo al restaurarlo, solo la sintaxis SQL.Errores típicos
LAB práctico — Política de respaldo 3-2-1 automatizada
Objetivo: dejar tienda_tlalli con respaldo diario automatizado, cifrado, con verificación SHA-256 y una copia fuera del sitio.
Requisitos: MySQL 8 y/o PostgreSQL 16 funcionando con la BD `tienda_tlalli`, ~30 MB libres en disco.
Paso 1 — Crear el usuario de respaldo
MySQL:
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'ClaveSegura_2026!';
GRANT SELECT, LOCK TABLES, RELOAD, SHOW VIEW, EVENT, TRIGGER ON tienda_tlalli. TO 'backup_user'@'localhost';
FLUSH PRIVILEGES;PostgreSQL:
CREATE ROLE backup_user WITH LOGIN PASSWORD 'ClaveSegura_2026!';
GRANT CONNECT ON DATABASE tienda_tlalli TO backup_user;
GRANT USAGE ON SCHEMA public TO backup_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup_user;Paso 2 — Script de respaldo Linux (adapta a Windows si aplica)
Guarda como `/opt/backups/backup_tienda.sh`:
#!/usr/bin/env bash
set -euo pipefail
FECHA=$(date +%Y-%m-%d_%H%M)
DEST=/opt/backups/tienda_tlalli
mkdir -p "$DEST"1. Dump MySQL
mysqldump -u backup_user -p"${MYSQL_PWD}" \
--single-transaction --routines --triggers --events \
--default-character-set=utf8mb4 \
tienda_tlalli | gzip > "$DEST/mysql_${FECHA}.sql.gz"2. Dump PostgreSQL (formato custom)
pg_dump -U backup_user -h localhost -F c \
-f "$DEST/pg_${FECHA}.dump" tienda_tlalli3. Hash SHA-256
sha256sum "$DEST/mysql_${FECHA}.sql.gz" "$DEST/pg_${FECHA}.dump" > "$DEST/${FECHA}.sha256"4. Cifrar con GPG (passphrase en variable de entorno)
echo "${BACKUP_GPG_PWD}" | gpg --batch --yes --passphrase-fd 0 \
--symmetric --cipher-algo AES256 \
"$DEST/mysql_${FECHA}.sql.gz"
echo "${BACKUP_GPG_PWD}" | gpg --batch --yes --passphrase-fd 0 \
--symmetric --cipher-algo AES256 \
"$DEST/pg_${FECHA}.dump"5. Copiar a S3 (fuera del sitio)
aws s3 cp "$DEST/mysql_${FECHA}.sql.gz.gpg" s3://ciberecus-backups/tienda/ 2>/dev/null || true
aws s3 cp "$DEST/pg_${FECHA}.dump.gpg" s3://ciberecus-backups/tienda/ 2>/dev/null || true6. Rotación 7 días
find "$DEST" -name ".gz" -mtime +7 -delete
find "$DEST" -name ".dump" -mtime +7 -delete
find "$DEST" -name ".gpg" -mtime +7 -deleteecho "[OK] Respaldo ${FECHA} completado."
Paso 3 — Probar la restauración
Restaurar en una base temporal:
# MySQL: crear BD de prueba, restaurar, contar filas
mysql -u root -p -e "CREATE DATABASE test_restore;"
gunzip -c /opt/backups/tienda_tlalli/mysql_2026-08-23_0200.sql.gz |
mysql -u root -p test_restore
mysql -u root -p -e "SELECT COUNT() FROM test_restore.pedidos;"
Esperado: 110
PostgreSQL: crear BD, restaurar, contar filas
createdb -U postgres test_restore_pg
pg_restore -U postgres -d test_restore_pg /opt/backups/tienda_tlalli/pg_2026-08-23_0200.dump
psql -U postgres -d test_restore_pg -c "SELECT COUNT() FROM pedidos;"
Esperado: 110
Paso 4 — Verificar SHA-256
cd /opt/backups/tienda_tlalli
sha256sum -c 2026-08-23_0200.sha256
Debe decir: OK
Si dice `FAILED`, el archivo está corrupto. Investiga antes del próximo ciclo.
Paso 5 — Automatizar con cron
chmod +x /opt/backups/backup_tienda.sh
crontab -e
Agregar:
0 2 * /opt/backups/backup_tienda.sh >> /var/log/backup_tienda.log 2>&1Entregables del LAB
- [ ] Script `backup_tienda.sh` ejecutándose sin errores.
- [ ] Archivos `.gz` y `.dump` generándose diariamente.
- [ ] Hash SHA-256 verificado en al menos 3 ciclos.
- [ ] Restauración probada en `test_restore` con conteo de filas correcto.
- [ ] Al menos una copia en S3 u otro servicio fuera del sitio.
Quest
Preguntas de opción múltiple (10)
1. ¿Cuál es la diferencia principal entre `mysqldump` y un respaldo físico de InnoDB?
A. `mysqldump` es más rápido en bases grandes. B. `mysqldump` produce SQL lógico, no copia los archivos de datos crudos. C. El respaldo físico solo funciona con MyISAM. D. No hay diferencia, son sinónimos.
2. ¿Por qué es importante usar `--single-transaction` en `mysqldump` con InnoDB?
A. Hace que el dump sea más pequeño. B. Genera un dump consistente sin bloquear lecturas ni escrituras prolongadas. C. Activa el modo de cifrado de la base. D. Reduce el uso de CPU a la mitad.
3. ¿Qué formato de `pg_dump` es mejor para restaurar tablas individuales?
A. `plain` B. `custom` C. `directory` D. `tar`
4. La regla 3-2-1 significa:
A. Tres respaldos, dos discos, una contraseña. B. Tres copias, dos medios, una fuera del sitio. C. Tres servidores, dos versiones, una licencia. D. Tres bases, dos motores, una aplicación.
5. ¿Qué opción de `mysqldump` es crítica en XAMPP/Windows para evitar acentos rotos?
A. `--no-data` B. `--default-character-set=utf8mb4` C. `--compress` D. `--skip-lock-tables`
6. ¿Por qué es importante generar y verificar el SHA-256 del respaldo?
A. Para acelerar la restauración. B. Para detectar corrupción o modificación del archivo. C. Para comprimir mejor el archivo. D. Para permitir la restauración remota.
7. ¿Qué usuario es el más seguro para correr respaldos automáticos en MySQL?
A. `root` B. Un usuario dedicado con permisos mínimos: `SELECT, LOCK TABLES, RELOAD`. C. El usuario de la aplicación. D. `admin`.
8. ¿Cuál es la frecuencia mínima recomendada para probar restauraciones?
A. Cada vez que se hace un respaldo. B. Una vez al mes. C. Una vez al año. D. Solo cuando hay un incidente.
9. En PostgreSQL, ¿qué formato de `pg_dump` es el más rápido para bases de terabytes?
A. `plain` B. `custom` C. `directory` con `-j N` workers. D. `tar`.
10. ¿Qué herramienta se usa típicamente para cifrar respaldos con AES-256 desde la línea de comandos?
A. `tar` B. `gpg` o `openssl`. C. `zip` con contraseña. D. `rsync`.
Preguntas abiertas (3)
A. Explica con tus palabras por qué un respaldo que nunca se ha probado no puede considerarse un respaldo válido. Da un ejemplo real (puede ser hipotético) de cuándo un DBA descubriría esto de la peor manera.
B. Compara los formatos `plain`, `custom` y `directory` de `pg_dump`. Para una base de datos de una tienda en línea de 500 GB con 200 tablas, ¿cuál elegirías y por qué? Considera tiempo de respaldo, tiempo de restauración y capacidad de restaurar tablas individuales.
C. Diseña una política de retención para tienda_tlalli (5 MB hoy, proyección 2 GB en 2 años) que cumpla 3-2-1. Especifica: dónde vive cada copia, cuánto tiempo se conserva, quién tiene acceso, y cómo se prueba la restauración.
Respuestas modelo
1. B — `mysqldump` produce SQL lógico (sentencias CREATE + INSERT), no copia los archivos `.ibd` ni los binlogs. El respaldo físico (con `mysqlbackup`, `mariabackup` o `xtrabackup`) clona el directorio de datos y es mucho más rápido para bases grandes. La A es falsa: el respaldo físico es más rápido en bases grandes. La C es falsa: existen para InnoDB. La D es falsa: son diferentes en propósito y rendimiento.
2. B — `--single-transaction` usa una transacción consistente (REPEATABLE READ) que evita bloqueos prolongados. Para InnoDB es la forma estándar de respaldar en producción sin downtime. La A es irrelevante al tamaño. La C es falsa: no cifra. La D es falsa: no reduce CPU.
3. B — El formato `custom` (binario comprimido) permite restaurar tablas individuales con `pg_restore --table=clientes`. El `plain` ejecuta todo en bloque. El `directory` permite extraer archivos específicos pero es más complejo. La D (`tar`) no es un formato de `pg_dump`.
4. B — 3 copias, 2 medios, 1 fuera del sitio. Es la definición adoptada por NIST y CIS. La A es una invención. La C y D son distractores sin relación con respaldos.
5. B — En Windows/XAMPP, el cliente `mysql.exe` y `mysqldump.exe` usan cp1252 por defecto. Sin `--default-character-set=utf8mb4`, los acentos (Xochitl, Ramírez) se pierden. La A es para no incluir datos, no para encoding. La C es para transmisión. La D cambia el comportamiento de bloqueos.
6. B — SHA-256 detecta si el archivo fue modificado, truncado o corrompido en tránsito o en disco. Es un checksum criptográfico que no se puede falsear sin que se note. La A no es su propósito. La C no comprime. La D no es función de hashing.
7. B — Principio de menor privilegio. El usuario de respaldo solo necesita `SELECT` (para leer datos), `LOCK TABLES` (para consistencia en MyISAM, opcional en InnoDB), `RELOAD` (para `FLUSH TABLES`), `SHOW VIEW`, `EVENT`, `TRIGGER`. `root` tiene más permisos de los necesarios y compromete toda la base si se filtra. La C es un riesgo similar.
8. B — Mensual. Cada respaldo es teórico hasta que se restaura. Una vez al mes es el balance entre costo operacional y detección temprana de problemas. La A es operacionalmente inviable. La C es riesgosa: un año de respaldos sin probar es un año de fe ciega. La D es tarde: ya hubo un incidente.
9. C — El formato `directory` con `-j N` workers paraleliza el respaldo y la restauración. Para 500 GB, la diferencia entre `plain` y `directory` con 8 workers puede ser de horas a minutos. La A es la más lenta. La B no paraleliza. La D no existe en `pg_dump`.
10. B — `gpg --symmetric` y `openssl enc -aes-256-cbc` son las dos herramientas estándar de cifrado desde línea de comandos. `tar` no cifra por defecto. `zip` con contraseña usa ZipCrypto (débil). `rsync` sincroniza, no cifra.
Tras las huellas — Por Citlalli
Cuando los españoles quemaron los archivos del calmécac en 1521, los frailes encontraron en los codex sobrevivientes los tributos de los últimos 50 años. Los mexicas no perdieron toda su historia porque tuvieron tres copias en lugares diferentes, incluido un archivo de tributos que se enviaba a otra provincia anualmente.
En tu trabajo de DBA vas a experimentar el equivalente moderno: un disco duro que falla, un servidor secuestrado por ransomware, una consulta que borra la tabla equivocada. En cada uno de esos momentos vas a agradecer haber automatizado los respaldos, haberlos probado y haber guardado una copia fuera del sitio.
Pregunta para el cuaderno: ¿qué pasa hoy mismo si se cae tu disco duro? ¿Cuánto tardarías en restaurar tienda_tlalli? ¿Tienes un respaldo probado de los últimos 7 días? Si la respuesta es "no sé" o "nunca lo he probado", este tema es tu día de comienzo.
Cierre del cuaderno
Líneas para tu cuaderno físico (escribe a mano, no en pantalla):