Migración de bases de datos PostgreSQL entre versiones mayores

Actualizar PostgreSQL a una versión mayor exige planificación técnica, validación de compatibilidad y una estrategia para minimizar el downtime. Esta guía explica métodos prácticos (pg_upgrade, dump/restore, replicación lógica y copias físicas), pasos de preparación, comandos esenciales y una checklist para validar la migración en producción.

Por qué una migración entre versiones mayores requiere atención

Las versiones mayores de PostgreSQL pueden introducir cambios en el formato físico del cluster, comportamiento interno, optimizaciones del planner, eliminación de funciones o cambios en extensiones. Estos cambios hacen que una simple sustitución binaria no siempre sea segura; por eso existen herramientas especializadas y flujos con distintos costes en tiempo de inactividad, complejidad y duración.

Planificación previa

  • Inventario: lista de bases de datos, tamaños, extensiones instaladas, tablaspaces, objetos large object y roles.
  • Compatibilidad: comprobar que las extensiones (PostGIS, citext, pg_trgm, etc.) tienen versiones compatibles en el nuevo PostgreSQL.
  • Locales y collations: verificar que la localización, encoding y collation del cluster son compatibles con la nueva versión.
  • Backups y snapshots: probar restauraciones a partir del backup que usarás para el rollback.
  • Pruebas en staging: replicar el entorno y ejecutar pg_upgrade –check o un dump/restore para medir tiempos.

Métodos de migración y cuándo usarlos

1) pg_upgrade (actualización in-place, más rápida)

pg_upgrade trabaja sobre los directorios de datos del clúster y solo convierte los metadatos internos, por lo que suele ser la opción más rápida para grandes volúmenes. Requisitos: binarios de las versiones antigua y nueva disponibles en el mismo host.

Flujo básico:

  • Instalar nuevos binarios de PostgreSQL.
  • Ejecutar: pg_upgrade --old-datadir /var/lib/pgsql/olddata --new-datadir /var/lib/pgsql/newdata --old-bindir /usr/old/bin --new-bindir /usr/new/bin --check para detectar problemas.
  • Hacer copia de seguridad completa (snapshot o pg_basebackup) y detener el servicio antiguo.
  • Ejecutar pg_upgrade (opción --link para reducir espacio y acelerar, si SO lo permite): pg_upgrade --old-datadir ... --new-datadir ... --old-bindir ... --new-bindir ... --link.
  • Recompilar extensiones y ejecutar ANALYZE y VACUUM FULL o al menos ANALYZE para actualizar estadísticas.

Ventajas: rapidez y menor uso de espacio. Riesgos: requiere binarios locales y cierto downtime para el corte final.

2) pg_dump / pg_restore (dump/restore lógico)

El volcado lógico es la opción más robusta cuando se cambia arquitectura, sistema operativo o hay incompatibilidades físicas. Genera un archivo lógico que se restaura en el nuevo servidor.

Comandos recomendados para grandes bases:

  • Roles y configuración global: pg_dumpall --globals-only > globals.sql
  • Dump por base con formato comprimido y paralelo: pg_dump -Fc -j 8 -f dbname.dump dbname
  • Restore: pg_restore -d dbname -j 8 dbname.dump

Ventajas: independencia de formato físico, limpieza de objetos obsoletos. Inconvenientes: tiempo de restauración y mayor downtime si no se replica previamente.

3) Replicación lógica / Zero-downtime

Para minimizar downtime, la replicación lógica (nativa desde PostgreSQL 10) o soluciones como pglogical permiten sincronizar datos hacia el nuevo servidor manteniendo la vieja instancia en producción hasta el corte final.

  • Crear publicación en origen: CREATE PUBLICATION pub_all FOR ALL TABLES;
  • En destino, crear suscripción: CREATE SUBSCRIPTION sub1 CONNECTION 'host=... dbname=... user=...' PUBLICATION pub_all;
  • Una vez sincronizado, desviar la aplicación al destino y finalizar la suscripción.

Ten en cuenta que la replicación lógica no replica objetos como roles, tablespaces ni large objects de la misma forma; necesitarás scripts adicionales (por ejemplo, pg_dumpall --globals-only y copiar tablespaces manualmente).

4) Copias físicas y pg_rewind

Si hiciste una copia física o utilizas replica streaming, puedes promover un standby y, si hay necesidad de volver atrás, usar pg_rewind para reconciliar diferencias con el cluster antiguo. Este flujo es útil cuando se desea reducir downtime y se dispone de replicación física previa.

Validaciones y tareas post-migración

  • Ejecutar ANALYZE en las bases para actualizar estadísticas y permitir al planner elegir planes eficientes.
  • Reindexar si detectas lentitud por índices bloat: REINDEX DATABASE dbname;
  • Verificar extensiones: SELECT extname, extversion FROM pg_extension;
  • Comprobar secuencias y valores máximos para evitar conflictos en inserciones: SELECT setval(...) cuando sea necesario.
  • Revisar logs y métricas (locks, long queries) y comparar tiempos de consultas críticas con benchmarks previos.

Estrategias para minimizar downtime

  • Replicación lógica para sincronizar y cortar en minutos en lugar de horas.
  • Pilotaje por schemas: replicar y promover schemas no críticos primero.
  • Usar pg_upgrade --link y validaciones rápidas en entornos de testing para reducir tiempo de corte.
  • Notificar ventanas de mantenimiento y tener scripts de rollback probados (p. ej. restaurar snapshot LVM o promover standby).

Problemas comunes y cómo resolverlos

  • Extensiones no disponibles: instalar versión compatible del paquete o compilar la extensión para la nueva versión.
  • Errores de encoding/collation: restaurar en cluster con la misma locale o volver a crear con dump/restore en otro locale y convertir datos si es necesario.
  • Owners y permisos: usar pg_dumpall --globals-only para roles y pg_restore --no-owner con cuidado.
  • Tablespaces en rutas diferentes: copiar datos y ajustar ALTER TABLE ... SET TABLESPACE o recrear tablespaces.

Buenas prácticas y checklist final

  • Hacer snapshot completo del sistema de ficheros o backup consistente antes de cualquier cambio.
  • Ejecutar pg_upgrade --check o restaurar un dump en staging para medir tiempos y problemas.
  • Probar rollback: restaurar desde backup o promover standby previamente preparado.
  • Documentar versiones de binarios, parámetros en postgresql.conf, tuneo de shared_buffers, work_mem y checkpoint segments.
  • Monitoreo: activar alertas en CPU, I/O, latencia y locks durante la ventana de migración.
  • Conservar el cluster antiguo al menos hasta validar completamente la integridad y rendimiento en producción.

Seguridad y cumplimiento durante la migración

Transfiere datos sobre canales cifrados y protege credenciales. Para buenas prácticas de seguridad y controles, consulta guías estándar como las OWASP Cheat Sheets. Mantén los backups cifrados y controla el acceso a las claves de cifrado.

Recursos y referencias

Para contexto técnico sobre PostgreSQL y su evolución entre versiones, la entrada en Wikipedia sobre PostgreSQL ofrece un panorama general. Usa documentación oficial de tu distribución y de las extensiones que utilices para pasos específicos de instalación y compatibilidad.

Conclusión

La migración entre versiones mayores de PostgreSQL es una tarea crítica que combina conocimiento del motor, planificación de infraestructuras y pruebas rigurosas. Selecciona la técnica adecuada según el tamaño de tus bases, requisitos de downtime y compatibilidad de extensiones: pg_upgrade para migraciones rápidas in-place, pg_dump/pg_restore para flexibilidad y limpieza, y replicación lógica para minimizar interrupciones. Antes del corte, realiza backups, pruebas en staging y una checklist de validación; después, monitorea y ajusta parámetros. Con preparación y pruebas, la migración puede ser segura, reversible y con impacto mínimo sobre tus aplicaciones.