Recuperación de Datos de Base de Datos PostgreSQL Corrupta
PostgreSQL es conocido por su robustez y cumplimiento ACID, pero ningún sistema de base de datos es inmune a la corrupción causada por fallos de hardware, cortes de corriente durante puntos de control o discos defectuosos. Cuando un clúster PostgreSQL queda inutilizable, existen técnicas especializadas que van desde la recuperación point-in-time mediante WAL hasta el análisis forense directo de heap files con pg_filedump.
Estructura de un Clúster PostgreSQL
Para entender cómo se produce la corrupción y cómo recuperarse, es esencial conocer la organización interna de los datos en PostgreSQL:
El directorio PGDATA
Todo el clúster PostgreSQL reside en un directorio raíz denominado PGDATA (típicamente /var/lib/postgresql/X/main/). Su estructura incluye:
- base/: Contiene un subdirectorio por cada base de datos identificado por su OID (Object Identifier). Dentro de cada subdirectorio, cada tabla e índice es un archivo (o conjunto de archivos de 1 GB) identificado por su OID.
- pg_wal/ (antes
pg_xlog/en versiones antiguas): Los archivos WAL (Write-Ahead Log) de 16 MB cada uno. Son el registro transaccional de PostgreSQL. - pg_tblspc/: Symlinks a tablespaces alternativos (para distribuir datos en diferentes discos).
- global/: Tablas del sistema compartidas entre todas las bases de datos (usuarios, roles, configuración).
- pg_control: Archivo crítico que contiene el estado del clúster, el LSN (Log Sequence Number) actual y otros metadatos esenciales.
Heap Files: Cómo Almacena PostgreSQL los Datos de las Tablas
Cada tabla en PostgreSQL se almacena como un heap file: un conjunto de páginas de 8 KB (por defecto) organizadas sin un orden específico de las filas. Cada página contiene:
- PageHeaderData: Metadatos de la página, incluyendo el LSN de la última modificación y el checksum (si está habilitado).
- ItemIdData: Array de punteros (line pointers) a los registros dentro de la página.
- Tuples: Los registros de datos propiamente dichos, con cabeceras de visibilidad MVCC.
Cuando una página se corrompe, PostgreSQL detecta el problema mediante el checksum de página (si estaba habilitado con initdb --data-checksums) y lanza el error invalid page in block X of relation base/Y/Z.
WAL y PITR: La Primera Línea de Recuperación
El Write-Ahead Log (WAL) de PostgreSQL es el mecanismo más importante para la recuperación de datos. Antes de escribir cualquier cambio en los archivos de datos, PostgreSQL escribe primero en el WAL. Esto permite:
Point-in-Time Recovery (PITR)
Con backups base y archivos WAL, es posible restaurar la base de datos a cualquier momento en el pasado:
- Restaurar el backup base: El último backup completo del clúster (obtenido con
pg_basebackupo similar). - Configurar recovery.conf / postgresql.conf: En PostgreSQL 12+ se usa
recovery_target_timeenpostgresql.confjunto con un archivorecovery.signal. - Reproducir los archivos WAL: PostgreSQL lee los WAL archivados hasta el punto de recuperación especificado.
- Promover el clúster: Una vez alcanzado el punto objetivo, PostgreSQL sale del modo de recuperación.
Para que PITR funcione, es imprescindible tener configurado archive_mode=on y archive_command ANTES del incidente.
pg_resetwal: El Último Recurso para WAL Corrupto
Cuando los archivos WAL están tan corruptos que PostgreSQL no puede arrancar, pg_resetwal (anteriormente pg_resetxlog) permite reinicializar el WAL y el archivo pg_control:
Advertencia crítica: pg_resetwal destruye los WAL no aplicados y puede dejar la base de datos en un estado inconsistente. PostgreSQL mismo advierte de esto con el mensaje "If you are not sure, stop now". Usar solo cuando:
- No hay backup disponible y la base de datos no puede arrancar de ninguna otra forma.
- Se acepta la pérdida de transacciones recientes no confirmadas en disco.
- Inmediatamente después se hará un
pg_dumpcompleto y se recreará el clúster.
El proceso tras usar pg_resetwal: arrancar PostgreSQL, exportar inmediatamente con pg_dump, crear un clúster nuevo con initdb y restaurar el dump.
zero_damaged_pages: Ignorar Páginas Corruptas
El parámetro zero_damaged_pages (configurable en postgresql.conf o con SET zero_damaged_pages = on) permite a PostgreSQL continuar operando cuando encuentra páginas corruptas, tratando las páginas dañadas como páginas en blanco en lugar de lanzar un error fatal.
Con este parámetro activado:
- PostgreSQL puede arrancar y responder a consultas incluso con páginas dañadas.
- Las filas en páginas corruptas se pierden (se tratan como espacio vacío), pero el resto de los datos permanece accesible.
- Es posible ejecutar
pg_dumppara exportar los datos accesibles.
Este enfoque minimiza la pérdida de datos: solo se pierden los registros en las páginas físicamente corruptas, no toda la tabla o base de datos.
pg_filedump: Análisis Forense de Páginas PostgreSQL
Cuando el servidor PostgreSQL no puede arrancar de ninguna forma, la herramienta pg_filedump permite examinar directamente los archivos de heap y los archivos WAL sin necesidad de un servidor en ejecución:
- Inspección de páginas: Muestra el contenido de cada página de 8 KB en formato legible, incluyendo cabeceras, line pointers y datos de tuples.
- Identificación de páginas válidas: Permite determinar qué páginas del archivo están intactas y cuáles están corruptas.
- Extracción de datos raw: En manos expertas, permite extraer los valores de las columnas directamente del formato binario de PostgreSQL.
El uso de pg_filedump requiere conocer el esquema de la tabla (tipos de datos y orden de columnas) para interpretar correctamente los bytes de cada tuple.
Escenarios de Corrupción Comunes en PostgreSQL
| Escenario | Archivos Afectados | Solución Recomendada |
|---|---|---|
| Corte de luz durante checkpoint | Páginas de heap parcialmente escritas | WAL replay o zero_damaged_pages |
| Disco defectuoso con sectores malos | Archivos heap y/o WAL | pg_filedump + extracción manual |
| WAL archivados incompletos | pg_wal/ | PITR hasta el último WAL disponible |
| pg_control corrupto | global/pg_control | pg_resetwal (con pérdida de datos) |
| Tablespace externo perdido | pg_tblspc/ links rotos | Restaurar tablespace o redirigir symlinks |
| Actualización de versión fallida | global/ y catálogos del sistema | Restaurar backup pre-actualización |
pg_dump en una Base de Datos Dañada
Si PostgreSQL puede arrancar (con o sin zero_damaged_pages), el primer objetivo debe ser ejecutar pg_dump para exportar todo lo que sea accesible:
pg_dump -U postgres -d mi_base_datos -f /backup/dump_recuperacion.sql --no-password
Consideraciones durante el dump de una base dañada:
- Si
pg_dumpfalla en una tabla específica, se puede intentar exportar tabla por tabla con-t nombre_tabla. - Las páginas corruptas con
zero_damaged_pagesactivo se exportan como vacías, no causan error en el dump. - Exportar también los roles y configuración global con
pg_dumpall --globals-only.
Proceso Completo de Recuperación PostgreSQL
- Detener PostgreSQL inmediatamente si sigue en ejecución con errores (evita daños adicionales).
- Hacer copia de todo PGDATA:
rsync -av /var/lib/postgresql/ /backup/pgdata_backup/ - Intentar PITR si hay backups base y WAL archivados disponibles.
- Si no hay PITR disponible, intentar arranque con
zero_damaged_pages = ony exportar conpg_dump. - Si el servidor no arranca, intentar
pg_resetwal(con conocimiento de las consecuencias) o recurrir apg_filedumppara recuperación forense. - Recrear el clúster con
initdby restaurar el dump obtenido. - Verificar la integridad de los datos recuperados comparando registros clave con fuentes alternativas.