Pyxis Logo
Inicio / Formación IBM / Artículo técnico

Recuperación asistida por IA de una tabla eliminada en Db2

Un escenario que muestra cómo un agente de IA respaldado por servidores MCP para Db2 investigó un incidente de eliminación de tabla, se encontró con una limitación de recuperación point-in-time de Db2 y cambió de estrategia para reconstruir los datos mediante lógica de negocio.

17 min de lectura
Publicado 2026-05-20
Pyxis editorial team
Califique este artículo
Calificación media: Sin calificación
Su calificación: Sin calificación
visualizaciones: 0
Ilustración abstracta de operaciones asistidas por IA sobre una base de datos Db2

Por qué este caso fue importante

Este artículo se basa en un ejemplo ocurrido en un entorno interno de pruebas donde habíamos construido:

  • Servidores MCP especializados que exponían herramientas de investigación y recuperación para Db2
  • Un agente de IA capaz de utilizar esas herramientas
  • Un catálogo de inyecciones controladas de fallos para provocar incidentes en la base de datos

El objetivo era simple: inyectar fallos realistas y comprobar si el agente podía ayudar a un DBA a investigar y recuperar.

Una de esas pruebas se volvió más interesante de lo esperado. Inyectamos un escenario de drop de tabla y el agente no se limitó a ejecutar algunos comandos de recuperación. Encontró una limitación real de recuperación en Db2, adaptó la estrategia para reconstruir los datos basándose en la lógica de negocio y aplicó esa estrategia. Esa es la parte no trivial que merece ser documentada.

Más tarde se identificó una solución mejor que la reconstrucción de los datos, pero todavía no estaba soportada por el MCP: analizar el contenido de los logs de Db2 para reconstruir el SQL responsable de modificar los datos perdidos y volver a ejecutarlo. Esa capacidad fue añadida y probada. El agente de IA consiguió entonces recuperar todos los datos perdidos a partir del log de Db2.


El fallo inyectado

El incidente fue inyectado con el siguiente script:

#!/bin/bash
# DL-01 — DROP TABLE (no dependents)
# Drops STORE.PAYMENTS — no views or FKs depend on it.
# Symptom: SQL0204N on any query referencing PAYMENTS.

set -e

CONTAINER=db2-primary
DB=LABDB

echo "==> [DL-01] Injecting: DROP TABLE STORE.PAYMENTS"

docker exec "$CONTAINER" su - db2inst1 -c "
db2 connect to $DB > /dev/null

db2 \"INSERT INTO STORE.AUDIT_LOG (EVENT_TYPE, TABLE_NAME, DESCRIPTION)
     VALUES ('INCIDENT_INJECTED', 'STORE.PAYMENTS', 'DL-01: PAYMENTS table dropped — no cascade')\"

db2 \"DROP TABLE STORE.PAYMENTS\"

db2 commit
db2 connect reset > /dev/null
"

echo ""
echo "==> [DL-01] Done."
echo "    Symptom : SQL0204N when querying STORE.PAYMENTS"
echo "    Evidence: SYSCAT.TABLES has no entry for PAYMENTS"
echo "              SYSCAT.INDEXES missing PAYMENTS indexes"
echo "              AUDIT_LOG has the incident marker"

Todo fue diseñado intencionadamente para obtener un incidente simple de drop de tabla:

  • Una única tabla eliminada, sin claves foráneas ni vistas dependientes que complicaran el diagnóstico.

A primera vista, parecía el tipo de caso que un playbook convencional de recuperación debería resolver bien.


La petición al agente

Después de la inyección, el usuario preguntó:

Parece que algunas tablas fueron eliminadas. Investiga, crea un plan y ejecuta el plan para efectuar la recuperación.

La formulación importa porque la petición no le dice al agente:

  • Qué tabla fue eliminada
  • Cómo ocurrió la eliminación
  • Qué camino de recuperación debía seguir

El agente tuvo que investigar, formar un plan a partir de la evidencia y solo después ejecutar la recuperación. En otras palabras, se trató de una tarea de razonamiento, no de una simple ejecución de comandos.


Lo primero que encontró el agente

El primer hallazgo útil estaba en la huella dejada por la propia inyección:

  • La tabla STORE.AUDIT_LOG contenía el marcador del incidente
  • El marcador identificaba STORE.PAYMENTS
  • La descripción confirmaba el incidente DL-01

El significado y la relevancia de esa tabla fueron inferidos por el agente. A partir de ahí, el agente comprobó que:

  • La tabla STORE.PAYMENTS ya no existía y la información esperada en el catálogo de Db2 había desaparecido
  • Los demás objetos del esquema STORE seguían presentes

Esto llevó a la primera conclusión importante: no se trataba de un fallo a nivel de todo el esquema, sino de la pérdida de un objeto específico. La investigación se centró entonces en ese objeto:

El agente resumió la situación así:

ÍtemDetalle
Tabla eliminadaSTORE.PAYMENTS
Número conocido de filas antes de la pérdida19 filas
Momento del drop2026-05-20 20:35:40
Backup disponibleBackup completo en /database/backup
Momento del backup20:19:51, antes de la creación del esquema STORE
Archive loggingLOGARCHMETH1=LOGRETAIN
Ubicación de los logs activos/database/data/db2inst1/NODE0000/SQL00001/LOGSTREAM0000/
Síntoma inmediatoSQL0204N al acceder a STORE.PAYMENTS
Evidencia adicionalMarcador en AUDIT_LOG, ausencia de entradas de catálogo para la tabla y sus índices

La primera vía de recuperación parecía prometedora

La siguiente pregunta era si la tabla podía recuperarse mediante el mecanismo normal de restore y rollforward de Db2.

El entorno tenía un backup completo en /database/backup y archive logging activo con LOGARCHMETH1=LOGRETAIN.

En ese momento, la situación parecía favorable. Un patrón común de recuperación sería:

PasoAcción de recuperación comúnObjetivo
1Restaurar la base de datos en una base de recuperación separadaTrabajar sobre una copia segura en lugar del sistema live
2Hacer rollforward hasta un punto inmediatamente anterior al dropRecuperar el estado del objeto antes del incidente
3Exportar la tabla recuperadaExtraer los datos recuperados de la base lateral
4Recrearla o importarla de nuevo en la base liveReponer el objeto recuperado en producción

Este es un enfoque técnico común y perfectamente razonable. Después de eso, el caso se volvió más interesante.


La primera complicación: el backup era demasiado antiguo

El backup existía, pero se había tomado antes de que todo el esquema STORE hubiera sido creado. Restaurar ese backup por sí solo no devolvería la tabla STORE.PAYMENTS. La única vía viable pasaba a depender del rollforward de los logs.

Aun así, seguía pareciendo que la tabla y los datos eran recuperables porque:

  • El archive logging estaba activo
  • Los logs necesarios para aplicar las modificaciones posteriores al backup se habían conservado

El plan construido por el agente

Con esa evidencia, el agente elaboró el siguiente plan:

PasoAcción planificadaResultado pretendido
1Restaurar el backup antiguo en una base de recuperación separadaEvitar tocar la base live durante la investigación
2Hacer rollforward de esa base hasta inmediatamente antes del dropRecrear el estado previo al incidente si la granularidad temporal de Db2 lo permitía
3Recuperar allí la definición y los datos de la tablaUsar la copia de recuperación como fuente de extracción
4Transferir el objeto recuperado a la base liveRestablecer el servicio en LABDB
5Validar integridad y limpiarDejar la base de producción en un estado fiable

Esto ya era un razonamiento sólido de DBA y, de no haber existido las limitaciones de Db2 en recuperación point-in-time, la historia habría terminado ahí.

Pero no terminó.


El verdadero problema en Db2: el PITR no conseguía aislar las filas

El agente restauró el backup en una base de datos de recuperación separada y aplicó rollforward.

Lo que encontró fue una dificultad sutil:

  • El DDL de CREATE TABLE STORE.PAYMENTS era recuperable
  • Sin embargo, las filas de la tabla no eran recuperables

¿Por qué?

Porque la carga de la tabla y el drop fueron ambos confirmados (committed) dentro del mismo segundo.

La línea temporal relevante era:

HoraEventoSignificado
20:32:09CREATE TABLE PAYMENTSLa definición de la tabla entra en los logs mucho antes del segundo crítico
20:35:40.xxxINSERT INTO PAYMENTS (...)Las 19 filas de pagos son confirmadas
20:35:40.995DROP TABLE PAYMENTSLa tabla es eliminada en el mismo segundo del insert

El rollforward point-in-time de Db2 acepta timestamps con granularidad mínima de un segundo.

Eso significa que las opciones reales de recuperación eran:

Objetivo de rollforwardResultado
20:35:39Todo lo confirmado hasta 20:35:39; las filas aún no existen
20:35:40Todo lo confirmado hasta 20:35:40; incluye el INSERT y el DROP
fin de los logsEl mismo resultado práctico que 20:35:40; el drop sigue prevaleciendo

No existía ningún objetivo válido que incluyera las filas insertadas pero excluyera el drop, y aquí está el verdadero corazón técnico del caso.

El fallo ya no era:

  • “¿Cómo recuperar una tabla eliminada?”

Pasaba a ser:

  • “¿Cómo recuperar cuando la recuperación física point-in-time puede devolver la estructura pero ya no el conjunto original de filas?”

Ese es un problema mucho más difícil.


Lo que Db2 todavía pudo recuperar

Incluso sin conseguir aislar las filas, la base de datos de recuperación todavía dio al agente algo muy valioso:

  • La definición exacta de la tabla
  • La clave primaria
  • La clave foránea
  • Los check constraints
  • La definición del índice existente sobre la tabla

El agente extrajo esa definición con db2look y recreó la tabla en la base de datos real. Después de eso, la estructura de la tabla estaba recuperada, pero los datos seguían faltando.

El agente no se detuvo. Fue más allá. El registro de ejecución hasta ese momento puede resumirse así:

PasoAcciónResultado
1Backup completo identificado en /database/backupBackup válido encontrado, pero demasiado antiguo para recuperación directa del objeto
2LOGARCHMETH1=LOGRETAIN confirmadoLa vía de recuperación por logs seguía siendo viable
3Logs archivados y activos consolidadosLa base de recuperación pasó a tener el material necesario para PITR
4Redirected restore a LABRCVRÉxito
5Rollforward hasta inmediatamente antes del dropDDL recuperable, datos aún ausentes
6db2look sobre LABRCVRDDL exacto extraído, incluyendo PK, FK, checks e índice
7Tabla recreada en LABDB liveEstructura restaurada con exactitud

El paso decisivo: reconstrucción de los datos recurriendo a la lógica de negocio

Esta es la parte más importante del artículo. El agente cambió el enfoque de “razonar sobre cómo efectuar la recuperación física” a “dado que la recuperación física no es posible, cómo efectuar la reconstrucción de los datos recurriendo a la lógica del negocio”.

Analizó el contexto transaccional superviviente en torno a STORE.PAYMENTS, en especial STORE.ORDERS, e infirió qué pedidos debían tener lógicamente pagos correspondientes.

La interpretación central fue:

  • Los pedidos DELIVERED ya deberían tener pagos válidos
  • Los pedidos SHIPPED también deberían tener pagos
  • Los pedidos PROCESSING, PENDING y CANCELLED no deberían tener pagos

Eso produjo:

Estado del pedidoPedidosFilas de pago reconstruidas
DELIVERED1–12, 31–3315
SHIPPED13–164
PROCESSING17–20, 36–370
PENDING21–26, 38–400
CANCELLED27–300

Total:

  • 15 + 4 = 19

El valor coincidía exactamente con el número conocido de filas perdidas.

El agente reconstruyó entonces:

  • AMOUNT a partir de STORE.ORDERS.TOTAL_AMOUNT
  • Fechas de pago plausibles en relación con las fechas de los pedidos
  • Métodos de pago permitidos
  • Valores válidos para el estado del pago
  • Referencias sintácticamente plausibles

En resumen:

  • La recuperación Db2 devolvió la definición de la tabla
  • El agente reconstruyó un conjunto de filas consistente con el negocio

Como regla general, una herramienta no basada en IA no conseguiría hacerlo por sí sola.

Es importante tener en cuenta que el agente, haciendo lo mejor que podía, no puede hacer milagros. La reconstrucción fue una medida de recurso y no puede compararse con una recuperación exacta y completa de los datos. Los valores originales de algunas columnas se perdieron definitivamente, entre ellos:

  • Timestamps exactos de los pagos
  • Métodos de pago originales por pedido
  • Identificadores de referencia originales

Los detalles de la recuperación y reconstrucción se muestran a continuación:

Aspecto¿Recuperación exacta?Observaciones
Definición de la tablaSíRecuperada de los logs y extraída con db2look
Clave primariaSíRecreada exactamente
Clave foráneaSíRecreada exactamente
Check constraintsSíRecreados exactamente
Definición del índiceSíRecreada exactamente
Importes originales de los pagosEn la práctica, síDerivados directamente de STORE.ORDERS.TOTAL_AMOUNT
Timestamps originales de los pagosNoReconstruidos como valores plausibles
Métodos originales de pagoNoReconstruidos dentro de los valores permitidos
Referencias originales de pagoNoReconstruidas como identificadores plausibles
Identidad y número original de filasParcialmenteEl recuento coincidió exactamente; el contenido fue fiel al negocio, no original

Validación después de la reconstrucción

El agente no se detuvo después de insertar 19 filas plausibles. Validó que:

  • La tabla recreada correspondía al DDL original
  • Las relaciones de clave foránea seguían siendo válidas
  • No existían referencias huérfanas
  • Las estadísticas habían sido refrescadas con RUNSTATS

El estado final quedó así:

  • STORE.PAYMENTS volvió a existir
  • 19 filas fueron repuestas de forma consistente con el negocio
  • La integridad referencial quedó limpia
  • La recuperación fue registrada en STORE.AUDIT_LOG

Desde el punto de vista operativo, la tabla volvió a ser utilizable.

El resumen final de la recuperación puede presentarse así:

PasoAcciónResultado
1Redirected restore y PITR a LABRCVRDefinición de la tabla recuperada, filas aún ausentes
2Extracción con db2look desde LABRCVRDDL exacto capturado
3Tabla recreada en LABDB liveEstructura de producción repuesta
419 filas reconstruidas a partir del patrón de negocio en STORE.ORDERSDatos faltantes repuestos de forma semánticamente consistente
5Validación de FK y verificaciones de integridadSin filas huérfanas
6Ejecución de RUNSTATSEstadísticas del optimizador refrescadas
7Registro de la recuperación y limpieza de la base temporalIncidente cerrado con trazabilidad

Una vía técnica mejor habría sido el análisis forense de logs

Aunque la reconstrucción tuvo éxito y tiene interés didáctico, ese no sería el camino habitual a seguir.

La mejor vía habría sido el análisis forense de los logs de Db2, porque eso habría permitido al agente extraer los valores originales de las filas insertadas en lugar de reconstruirlos a partir de la lógica de negocio.

En el momento del incidente, esa opción no estaba disponible a través de los servidores MCP. Las herramientas expuestas por el servidor soportaban investigación, restore, rollforward, inspección del catálogo y extracción de DDL, pero todavía no exponían utilidades forenses para leer el contenido bruto de los logs.

Esa limitación ya ha sido corregida.

db2fmtlog puede ayudar a formatear e inspeccionar contenido de bajo nivel de los logs durante la investigación forense.

Las capacidades que se añadieron son:

HerramientaObjetivo
forensic_log_formatEnvuelve db2fmtlog para formatear contenido de bajo nivel de los logs, de modo que pueda ser inspeccionado y consumido por el workflow de recuperación
recover_dropped_tableOrquesta tanto la ruta estándar RESTORE → ROLLFORWARD → EXPORT como la ruta forense cuando se detecta una colisión en el mismo segundo

Para este incidente en la tabla PAYMENTS, el workflow mejorado habría funcionado de la siguiente manera:

PasoAcción del workflow forenseObjetivo
1Detectar que el INSERT y el DROP colisionaron dentro del mismo segundoReconocer que el PITR no consigue aislar las filas deseadas
2Reunir la ventana exacta de logs y los metadatos del objetoDelimitar la investigación forense a la evidencia relevante
3Realizar un análisis forense real de bajo nivel sobre esa ventanaRecuperar los valores originales insertados si la herramienta subyacente, incluida la inspección formateada con db2fmtlog, lo permite
4Decodificar los valores recuperados basándose en el DDL de PAYMENTSVolver a mapear los valores a columnas y tipos Db2
5Reconstruir sentencias INSERT exactas con los valores originalesRecuperar las filas reales en lugar de aproximarlas

El resultado esperado sería:

EtapaResultado forense
Clasificación del incidenteColisión en el mismo segundo detectada y señalada como no resoluble solo mediante PITR
Extracción forenseValores originales de las filas recuperados, si la vía de análisis de bajo nivel los soporta
Decodificación guiada por DDLColumnas mapeadas de nuevo a PAYMENT_ID, ORDER_ID, PAYMENT_DATE, AMOUNT, METHOD, STATUS y REFERENCE
Recuperación finalINSERT exactos reconstruidos con los timestamps, métodos y referencias originales

La mejora esencial que ahora se ha añadido es:

  • la lógica estándar de recuperación Db2 sigue siendo la primera ruta
  • la investigación forense basada en db2fmtlog pasa a ser el fallback cuando el PITR de Db2 no puede aislar las filas deseadas

Con esta adición, el agente ya no necesita detenerse en la reconstrucción fiel al negocio en escenarios de colisión dentro del mismo segundo. Puede intentar primero la recuperación exacta de las filas y recurrir a la reconstrucción solo si la extracción forense también falla.


Lo que ocurrió después de la mejora del MCP

Después de la mejora del MCP server, el incidente fue revisitado y el agente consiguió recuperar la tabla con las 19 filas originales exactas, en lugar de solo con una aproximación basada en el análisis de datos de negocio.

Esta segunda recuperación es importante porque muestra la diferencia entre:

  • Una reconstrucción estructuralmente correcta
  • Y una recuperación verdadera de los valores originales de las filas

El post-mortem puede resumirse así:

TemaLo que ocurrió
IncidenteSTORE.PAYMENTS fue eliminada en 2026-05-20 20:35:40
Resultado inicialLa tabla fue reconstruida con 19 filas aproximadas basadas en la lógica de negocio
Problema restanteAlgunos valores reconstruidos eran incorrectos, en especial la fila 13 y las filas 17–19
Limitación de baseEl PITR seguía sin poder aislar el INSERT del DROP, porque ambos ocurrieron dentro del mismo segundo
Camino mejoradoSe utilizó análisis del contenido de los logs para recuperar los valores originales

1. Identificar el fichero de log correcto

El primer problema práctico fue la escala de los datos:

  • Existían 26 ficheros de log entre ubicaciones activas y archivadas
  • Una pasada forense sin filtros producía demasiado output

El agente utilizó las secciones XHDR de la salida de db2fmtlog para mapear intervalos de LFS a ficheros físicos. Los registros INSERT de interés tenían:

  • LFS = 6976

Eso permitió mapear la actividad relevante a:

  • S0000007.LOG

que resultó ser un fichero del log activo.

2. Localizar exactamente los 19 registros INSERT

El siguiente paso fue encontrar los offsets exactos en bytes de las 19 filas insertadas dentro de S0000007.LOG.

db2fmtlog proporcionó la metainformación necesaria para orientar la búsqueda:

  • Transaction ID
  • Tipo de registro
  • Identidad del objeto

La búsqueda binaria pasó entonces a buscar la combinación de:

  • Los bytes de TID de la transacción 000000000C6D
  • El marcador INSREC_DP asociado al objeto de destino

Esto permitió localizar los 19 registros en offsets exactos dentro del fichero de log de 16 KB.

3. Decodificar correctamente el formato de la fila

Este fue el paso más difícil, porque el formato interno de la fila en Db2 no estaba documentado de una manera amigable.

Fue necesario corregir varios errores de decodificación:

Sub-problemaError inicialCorrección final
Offset de inicio de la filaLos primeros offsets producían basuraEl inicio correcto de la fila era pos + 32
Decodificación de DECIMAL(12,2)El packed BCD se leyó con alineación de nibble incorrectaEl primer nibble era padding y debía ignorarse
Columnas de longitud variableInicialmente se asumió una tabla de offsets al finalLa fila real usaba campos explícitos de 2 bytes con longitudes antes del texto
Timestamp de la fila 13Los bytes donde se esperaba el timestamp mostraban BCD inválidoLa fila 13 contenía 20 bytes extra de overhead de log antes de los datos de columna

Después de estas correcciones, el agente consiguió decodificar correctamente los valores originales.

4. Aplicar los valores exactos de vuelta a la tabla live

Todavía fue necesario resolver dos problemas operativos:

ProblemaResolución
La herramienta execute_query solo permitía instrucciones SELECTEl DML se aplicó mediante docker exec y el CLP de Db2
Eran necesarios INSERT explícitos en una columna identity GENERATED ALWAYSPAYMENT_ID fue cambiada temporalmente a GENERATED BY DEFAULT y luego restaurada a GENERATED ALWAYS

La vía final de inserción utilizó un fichero de script CLP con db2 -tvf, porque el quoting directo de INSERT multilínea mediante docker exec ... bash -c se volvió demasiado frágil.

Lo que la primera reconstrucción había supuesto erróneamente

La reconstrucción inicial basada en lógica de negocio fue suficiente para restaurar el servicio, pero no era exacta.

La recuperación forense posterior mostró claramente las diferencias:

FilaCampoReconstruidoReal
13PAYMENT_DATE09:0017:00
13AMOUNT99.98179.99
17ORDER_ID1731
18ORDER_ID1832
19ORDER_ID1933

La primera reconstrucción había acertado:

  • Los patrones de los métodos
  • Los valores de estado
  • La estructura de las referencias

Pero seguía fallando en valores originales exactos, en puntos a los que solo un análisis real de bajo nivel sobre los logs podía llegar.

Estado final después de la recuperación mejorada

Después de la mejora del MCP y de la nueva ruta de recuperación:

  • STORE.PAYMENTS volvió a contener 19 filas
  • Las filas correspondían a los valores originales exactos existentes antes del drop
  • El comportamiento de la identity fue restaurado a GENERATED ALWAYS
  • No fue necesario ningún restore de backup para la etapa final de recuperación exacta de los datos

Esto hace que la lección principal del caso sea todavía más clara:

  • La reconstrucción mediante lógica de negocio ya era valiosa
  • Pero, después de la mejora del MCP, el agente consiguió ir más allá de la aproximación y recuperar exactamente el conjunto original de filas

Por qué este caso importa

Cuando la recuperación convencional encontró un límite real de Db2, el agente de IA consiguió seguir razonando sobre la semántica del negocio en lugar de detenerse en la frontera técnica.

Esto exigió tres niveles distintos de capacidad:

1. Herramientas de investigación Db2

  • Verificaciones de catálogo
  • Inspección de configuración
  • Comprensión de backups y logs
  • Ejecución de recuperación

2. Conocimiento de recuperación en Db2

  • Redirected restore
  • Limitaciones del rollforward
  • Extracción de DDL
  • Pasos de validación

3. Inferencia mediante lógica de negocio

  • Comprender qué representa una tabla de pagos
  • Inferir qué pedidos deberían tener pagos
  • Reconstruir filas coherentes con la evidencia de negocio superviviente

Cuando el MCP server mejoró, esa pila de capacidades se amplió con análisis forense real de bajo nivel. La combinación se volvió aún más fuerte:

  • conocimiento de recuperación Db2 para restaurar la estructura
  • razonamiento de lógica de negocio para proporcionar una vía de fallback
  • análisis forense de logs para recuperar los valores exactos cuando la vía estándar no podía

Eso es lo que hizo que el resultado final fuera especialmente relevante:

  • el agente primero restauró el servicio mediante reconstrucción
  • y más tarde, una vez mejorada la herramienta MCP, recuperó exactamente las filas originales

Referencias

Más en esta área

Más en esta área

Volver a formación
Categoría de artículos

Artículos de Db2 LUW

Consulta todos los artículos técnicos de Db2 LUW en una sola página de categoría.

Abrir categoría Db2 LUW