Migración de Bases de Datos Entre Servidores: Guía Completa con Cero Downtime
La migración de bases de datos es una operación crítica que requiere precisión, planificación y experiencia. Ya sea que esté actualizando infraestructura, consolidando bases de datos o migrando a un nuevo proveedor de hosting, migrar bases de datos entre servidores sin pérdida de datos o tiempo de inactividad extendido es esencial para mantener la continuidad del negocio. Esta guía completa cubre estrategias de migración para MySQL/MariaDB, PostgreSQL y MongoDB con énfasis en técnicas de cero downtime.
Entendiendo los Desafíos de la Migración de Bases de Datos
La migración de bases de datos presenta desafíos únicos comparada con migraciones basadas en archivos:
- Consistencia de Datos: Asegurar que no se pierdan datos durante la transferencia
- Requisitos de Downtime: Minimizar la interrupción del servicio
- Integridad Referencial: Mantener relaciones entre tablas
- Impacto en Rendimiento: Gestionar uso de recursos durante la migración
- Dependencias de Aplicaciones: Coordinar con cadenas de conexión de aplicaciones
- Manejo de Conjuntos de Datos Grandes: Transferir eficientemente terabytes de datos
- Codificación de Caracteres: Preservar codificación de datos entre sistemas
Una migración exitosa de base de datos requiere entender estos desafíos e implementar estrategias apropiadas para mitigar riesgos.
Planificación y Evaluación Pre-Migración
Evaluación del Entorno de Base de Datos
Antes de comenzar, realice una evaluación exhaustiva:
# MySQL/MariaDB assessment
mysql -u root -p << 'EOF'
-- Check database sizes
SELECT
table_schema AS 'Database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;
-- Check table engines
SELECT
table_schema,
table_name,
engine,
table_rows
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys');
-- Check stored procedures and functions
SELECT routine_schema, routine_name, routine_type
FROM information_schema.routines
WHERE routine_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys');
-- Check triggers
SELECT trigger_schema, trigger_name, event_object_table
FROM information_schema.triggers;
-- Check views
SELECT table_schema, table_name
FROM information_schema.views
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys');
EOF
Para PostgreSQL:
# PostgreSQL assessment
psql -U postgres << 'EOF'
-- Check database sizes
SELECT
datname,
pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;
-- List all schemas
\dn
-- List all tables with sizes
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- Check extensions
SELECT extname, extversion FROM pg_extension;
EOF
Elección de la Estrategia de Migración Correcta
Seleccione una estrategia basada en sus requisitos:
- Dump y Restauración: Simple, requiere downtime
- Basada en Replicación: Cero downtime, configuración compleja
- Replicación Lógica: PostgreSQL 10+, replicación selectiva
- Respaldo Físico: Rápido para bases de datos grandes
- Herramientas Nativas en la Nube: AWS DMS, Azure Database Migration Service
Checklist de Pre-Migración
Complete este checklist antes de proceder:
- Documentar versión y configuración actual de base de datos
- Calcular tamaño total de base de datos y tasa de crecimiento
- Identificar períodos de uso pico y valle
- Listar todas las bases de datos, usuarios y permisos
- Documentar cadenas de conexión de aplicaciones
- Crear respaldo completo de base de datos origen
- Probar procedimiento de restauración de respaldo
- Verificar que servidor destino cumple requisitos de recursos
- Asegurar conectividad de red entre servidores
- Instalar versión igual o compatible de base de datos en destino
- Configurar reglas de firewall para comunicación de base de datos
- Preparar plan de rollback
- Configurar monitoreo en ambos servidores
- Programar migración durante ventana de mantenimiento
- Notificar a interesados y equipos de aplicaciones
Métodos de Migración MySQL/MariaDB
Método 1: Dump y Restauración Tradicional
Para bases de datos más pequeñas o cuando el downtime es aceptable:
# On source server: Create full backup
mysqldump -u root -p \
--single-transaction \
--quick \
--lock-tables=false \
--routines \
--triggers \
--events \
--all-databases \
> /tmp/full_backup.sql
# Compress for faster transfer
gzip /tmp/full_backup.sql
# Transfer to new server
scp /tmp/full_backup.sql.gz user@new-server:/tmp/
# On new server: Extract and restore
gunzip /tmp/full_backup.sql.gz
mysql -u root -p < /tmp/full_backup.sql
# Verify restoration
mysql -u root -p << 'EOF'
SHOW DATABASES;
SELECT COUNT(*) FROM database_name.table_name;
EOF
Método 2: Migración con Replicación de Cero Downtime
Este es el enfoque recomendado para bases de datos de producción:
# Step 1: Configure source server for replication
# Edit MySQL configuration
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# Add or modify these settings:
# server-id = 1
# log_bin = /var/log/mysql/mysql-bin.log
# binlog_format = ROW
# binlog_do_db = your_database_name
# Restart MySQL
sudo systemctl restart mysql
# Create replication user
mysql -u root -p << 'EOF'
CREATE USER 'replicator'@'%' IDENTIFIED BY 'strong_replication_password';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';
FLUSH PRIVILEGES;
EOF
# Step 2: Create initial data snapshot
mysql -u root -p << 'EOF'
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
EOF
# Note the File and Position values - you'll need these!
# Example output:
# File: mysql-bin.000003
# Position: 73
# In another terminal, create backup
mysqldump -u root -p \
--single-transaction \
--master-data=2 \
--routines \
--triggers \
--events \
your_database_name > /tmp/initial_snapshot.sql
# Unlock tables
mysql -u root -p -e "UNLOCK TABLES;"
# Transfer snapshot
scp /tmp/initial_snapshot.sql user@new-server:/tmp/
# Step 3: Configure new server as replica
# On new server, edit configuration
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# Add these settings:
# server-id = 2
# relay_log = /var/log/mysql/mysql-relay-bin
# log_bin = /var/log/mysql/mysql-bin.log
# read_only = 1
sudo systemctl restart mysql
# Create database and import snapshot
mysql -u root -p << 'EOF'
CREATE DATABASE your_database_name;
EOF
mysql -u root -p your_database_name < /tmp/initial_snapshot.sql
# Configure replication
mysql -u root -p << 'EOF'
CHANGE MASTER TO
MASTER_HOST='source-server-ip',
MASTER_USER='replicator',
MASTER_PASSWORD='strong_replication_password',
MASTER_LOG_FILE='mysql-bin.000003', -- From SHOW MASTER STATUS
MASTER_LOG_POS=73; -- From SHOW MASTER STATUS
START SLAVE;
EOF
# Step 4: Monitor replication
mysql -u root -p << 'EOF'
SHOW SLAVE STATUS\G
EOF
# Key fields to check:
# Slave_IO_Running: Yes
# Slave_SQL_Running: Yes
# Seconds_Behind_Master: 0 (should decrease to 0)
# Last_Error: (should be empty)
# Continuous monitoring script
cat > /root/check-replication.sh << 'EOF'
#!/bin/bash
while true; do
mysql -u root -p'your_password' -e "SHOW SLAVE STATUS\G" | \
grep -E "(Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_Error)"
sleep 10
done
EOF
chmod +x /root/check-replication.sh
Método 3: Percona XtraBackup (Respaldo en Caliente)
Para bases de datos InnoDB grandes que requieren mínimo downtime:
# On source server: Install Percona XtraBackup
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo apt update
sudo apt install percona-xtrabackup-80 -y
# Create backup
sudo xtrabackup --backup \
--user=root \
--password=your_password \
--target-dir=/backup/mysql
# Prepare backup
sudo xtrabackup --prepare --target-dir=/backup/mysql
# Transfer to new server
rsync -avz --progress /backup/mysql/ user@new-server:/backup/mysql/
# On new server: Stop MySQL and restore
sudo systemctl stop mysql
sudo rm -rf /var/lib/mysql/*
sudo xtrabackup --copy-back --target-dir=/backup/mysql
sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl start mysql
Métodos de Migración PostgreSQL
Método 1: pg_dump y pg_restore
Enfoque estándar para PostgreSQL:
# On source server: Create backup
pg_dump -U postgres \
-F c \
-b \
-v \
-f /tmp/database_backup.dump \
database_name
# For all databases
pg_dumpall -U postgres \
--clean \
--if-exists \
-f /tmp/all_databases.sql
# Transfer to new server
scp /tmp/database_backup.dump user@new-server:/tmp/
# On new server: Restore
createdb -U postgres database_name
pg_restore -U postgres \
-d database_name \
-v \
/tmp/database_backup.dump
# Verify
psql -U postgres database_name << 'EOF'
\dt
SELECT COUNT(*) FROM your_table;
EOF
Método 2: Replicación por Streaming de PostgreSQL
Para migración PostgreSQL con cero downtime:
# On source server (primary): Configure replication
sudo nano /etc/postgresql/14/main/postgresql.conf
# Modify these settings:
# listen_addresses = '*'
# wal_level = replica
# max_wal_senders = 10
# max_replication_slots = 10
# hot_standby = on
# Configure authentication
sudo nano /etc/postgresql/14/main/pg_hba.conf
# Add this line:
# host replication replicator new-server-ip/32 md5
# Create replication user
sudo -u postgres psql << 'EOF'
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'replication_password';
EOF
# Restart PostgreSQL
sudo systemctl restart postgresql
# On new server (standby): Stop PostgreSQL
sudo systemctl stop postgresql
# Remove existing data directory
sudo rm -rf /var/lib/postgresql/14/main/*
# Create base backup using pg_basebackup
sudo -u postgres pg_basebackup \
-h source-server-ip \
-D /var/lib/postgresql/14/main \
-U replicator \
-P \
-v \
-R \
-X stream \
-C -S replica_slot
# The -R flag automatically creates standby.signal
# and configures recovery parameters
# Start PostgreSQL on standby
sudo systemctl start postgresql
# Verify replication status
# On primary:
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"
# On standby:
sudo -u postgres psql -c "SELECT * FROM pg_stat_wal_receiver;"
Método 3: Replicación Lógica (PostgreSQL 10+)
Para replicación selectiva o migración entre versiones:
# On source server: Enable logical replication
sudo nano /etc/postgresql/14/main/postgresql.conf
# Set:
# wal_level = logical
# max_replication_slots = 10
# max_wal_senders = 10
sudo systemctl restart postgresql
# Create publication
sudo -u postgres psql database_name << 'EOF'
CREATE PUBLICATION my_publication FOR ALL TABLES;
EOF
# On new server: Create subscription
sudo -u postgres psql database_name << 'EOF'
CREATE SUBSCRIPTION my_subscription
CONNECTION 'host=source-server-ip port=5432 dbname=database_name user=postgres password=password'
PUBLICATION my_publication;
EOF
# Monitor replication
sudo -u postgres psql database_name << 'EOF'
SELECT * FROM pg_stat_subscription;
EOF
Métodos de Migración MongoDB
Método 1: mongodump y mongorestore
# On source server: Create backup
mongodump \
--host localhost \
--port 27017 \
--username admin \
--password password \
--authenticationDatabase admin \
--out /tmp/mongodb_backup
# Transfer to new server
rsync -avz /tmp/mongodb_backup/ user@new-server:/tmp/mongodb_backup/
# On new server: Restore
mongorestore \
--host localhost \
--port 27017 \
--username admin \
--password password \
--authenticationDatabase admin \
/tmp/mongodb_backup
Método 2: Migración con Replica Set de MongoDB
# Add new server as replica set member
mongo --host primary-server << 'EOF'
rs.add("new-server:27017")
EOF
# Wait for synchronization
mongo --host new-server << 'EOF'
rs.status()
EOF
# When synced, step down primary and promote new member
mongo --host primary-server << 'EOF'
rs.stepDown()
EOF
Realizando el Cambio
Actualización de Configuración de Aplicación
# Update application database configuration
# Example for PHP application
sudo nano /var/www/html/config.php
# Change:
# define('DB_HOST', 'old-server-ip');
# To:
# define('DB_HOST', 'new-server-ip');
# For environment variable based apps
sudo nano /etc/environment
# Update:
# DB_HOST=new-server-ip
# Restart application services
sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx
Sincronización Final
# For replication-based migration:
# MySQL: Stop writes on source
mysql -u root -p << 'EOF'
SET GLOBAL read_only = ON;
FLUSH TABLES WITH READ LOCK;
EOF
# Wait for replica to catch up
mysql -u root -p -h new-server -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master
# Should show: Seconds_Behind_Master: 0
# Stop replication on new server
mysql -u root -p -h new-server << 'EOF'
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;
EOF
# For PostgreSQL: Promote standby to primary
sudo -u postgres /usr/lib/postgresql/14/bin/pg_ctl promote \
-D /var/lib/postgresql/14/main
# Verify promotion
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"
# Should return: f (false)
Verificación Post-Migración
Verificación de Integridad de Datos
# MySQL verification
# Compare row counts
mysql -u root -p << 'EOF'
SELECT
TABLE_SCHEMA,
TABLE_NAME,
TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database'
ORDER BY TABLE_NAME;
EOF
# Compare checksums
mysql -u root -p << 'EOF'
CHECKSUM TABLE your_database.your_table EXTENDED;
EOF
# PostgreSQL verification
sudo -u postgres psql database_name << 'EOF'
SELECT
schemaname,
tablename,
n_tup_ins,
n_tup_upd,
n_tup_del
FROM pg_stat_user_tables
ORDER BY tablename;
EOF
Pruebas de Rendimiento
# MySQL performance baseline
sysbench /usr/share/sysbench/oltp_read_write.lua \
--mysql-host=new-server-ip \
--mysql-user=root \
--mysql-password=password \
--mysql-db=testdb \
--tables=10 \
--table-size=100000 \
prepare
sysbench /usr/share/sysbench/oltp_read_write.lua \
--mysql-host=new-server-ip \
--mysql-user=root \
--mysql-password=password \
--mysql-db=testdb \
--tables=10 \
--table-size=100000 \
--threads=10 \
--time=60 \
run
# PostgreSQL performance test
pgbench -i -s 50 database_name
pgbench -c 10 -j 2 -t 10000 database_name
Procedimientos de Rollback
Si la migración falla, ejecute rollback:
# For replication-based migration:
# 1. Re-enable writes on source
mysql -u root -p << 'EOF'
SET GLOBAL read_only = OFF;
UNLOCK TABLES;
EOF
# 2. Update application to use old server
sudo nano /var/www/html/config.php
# Revert DB_HOST to old-server-ip
sudo systemctl restart php8.2-fpm nginx
# 3. Stop services on new server
sudo systemctl stop mysql
# 4. Document issues for analysis
echo "Migration failed at $(date): [reason]" >> /var/log/migration.log
Estrategias de Mitigación de Riesgos
Connection Pooling Durante Migración
# Configure ProxySQL for connection management
# Install ProxySQL
wget https://github.com/sysown/proxysql/releases/download/v2.5.0/proxysql_2.5.0-ubuntu20_amd64.deb
sudo dpkg -i proxysql_2.5.0-ubuntu20_amd64.deb
# Configure backend servers
mysql -u admin -padmin -h 127.0.0.1 -P6032 << 'EOF'
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (0, 'old-server', 3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (1, 'new-server', 3306);
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
EOF
# Applications connect to ProxySQL, which routes to appropriate backend
Monitoreo Durante Migración
# Create monitoring script
cat > /root/monitor-migration.sh << 'EOF'
#!/bin/bash
LOG="/var/log/migration-monitor.log"
while true; do
echo "=== $(date) ===" >> $LOG
# Check replication lag
LAG=$(mysql -u root -ppassword -e "SHOW SLAVE STATUS\G" | \
grep Seconds_Behind_Master | awk '{print $2}')
echo "Replication Lag: $LAG seconds" >> $LOG
# Check connections
CONN=$(mysql -u root -ppassword -e "SHOW STATUS LIKE 'Threads_connected';" | \
awk 'NR==2 {print $2}')
echo "Active Connections: $CONN" >> $LOG
# Check disk space
DISK=$(df -h /var/lib/mysql | awk 'NR==2 {print $5}')
echo "Disk Usage: $DISK" >> $LOG
# Alert if issues detected
if [ "$LAG" -gt 10 ]; then
echo "ALERT: High replication lag!" | mail -s "Migration Alert" [email protected]
fi
sleep 60
done
EOF
chmod +x /root/monitor-migration.sh
nohup /root/monitor-migration.sh &
Optimización de Rendimiento Después de Migración
# MySQL optimization
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# Add optimized settings:
# innodb_buffer_pool_size = 4G # 70-80% of available RAM
# innodb_log_file_size = 1G
# innodb_flush_log_at_trx_commit = 2
# innodb_flush_method = O_DIRECT
# innodb_file_per_table = 1
# max_connections = 500
# query_cache_type = 0
# query_cache_size = 0
sudo systemctl restart mysql
# PostgreSQL optimization
sudo nano /etc/postgresql/14/main/postgresql.conf
# Optimize settings:
# shared_buffers = 4GB
# effective_cache_size = 12GB
# maintenance_work_mem = 1GB
# checkpoint_completion_target = 0.9
# wal_buffers = 16MB
# default_statistics_target = 100
# random_page_cost = 1.1
# effective_io_concurrency = 200
# work_mem = 20MB
# min_wal_size = 1GB
# max_wal_size = 4GB
sudo systemctl restart postgresql
Conclusión
La migración exitosa de bases de datos entre servidores requiere planificación meticulosa, herramientas apropiadas y ejecución cuidadosa. Conclusiones clave de esta guía:
- Elija la estrategia de migración correcta basada en tamaño de base de datos, downtime aceptable y requisitos técnicos
- Use métodos basados en replicación para migraciones con cero downtime en entornos de producción
- Pruebe extensivamente antes del cambio, incluyendo conectividad de aplicación y rendimiento
- Monitoree continuamente durante la migración para detectar y atender problemas inmediatamente
- Mantenga procedimientos de rollback completos para asegurar continuidad del negocio
- Verifique integridad de datos post-migración usando checksums y conteos de filas
- Optimice rendimiento después de la migración para aprovechar nueva infraestructura
Ya sea migrando MySQL, PostgreSQL o MongoDB, los principios permanecen consistentes: respaldar, probar, replicar, verificar y optimizar. Con ejecución adecuada, la migración de base de datos puede completarse con riesgo mínimo y cero downtime, asegurando disponibilidad continua del servicio para sus aplicaciones y usuarios.
Recuerde siempre que la migración de base de datos no es solo un desafío técnico sino una operación crítica para el negocio. Planifique conservadoramente, pruebe exhaustivamente y ejecute metódicamente para lograr resultados exitosos.


