Pyxis Logo
Início / Formação IBM / Artigo técnico

Recuperação assistida por IA de uma tabela eliminada no Db2

Um cenário que mostra como um agente de IA suportado por servidores MCP para Db2 investigou um incidente de eliminação de tabela, deparou-se com uma limitação de recuperação para ponto no tempo do Db2 e mudou a estratégia para reconstrução dos dados através de análise da lógica de negócio.

17 min de leitura
Publicado 2026-05-20
Pyxis editorial team
Classifique este artigo
Classificação média: Sem classificação
A sua classificação: Sem classificação
visualizações: 0
Ilustração abstrata de operações assistidas por IA sobre uma base de dados Db2

Porque é que este caso foi importante

Este artigo baseia-se num exemplo que ocorreu num ambiente interno de testes onde tínhamos construído:

  • Servidores MCP especializados que expunham ferramentas de investigação e recuperação para Db2
  • Um agente de IA capaz de utilizar essas ferramentas
  • Um catálogo de injeções controladas de falhas para provocar incidentes na base de dados

O objetivo era simples: injetar falhas realistas e verificar se o agente conseguia ajudar um DBA a investigar e recuperar.

Um desses testes tornou-se mais interessante do que esperávamos. Injectámos um cenário de drop de tabela e o agente não se limitou a executar alguns comandos de recuperação. Encontrou uma limitação real de recuperação no Db2, adaptou a estratégia de forma a reconstruir os dados com base na lógica de negócio e aplicou-a. É essa a parte que não é trivial e que vale a pena documentar.

Mais tarde, identificou-se uma solução melhor que a reconstrução dos dados mas que não estava ainda sendo suportada pelos MCP: efetuar análise dos logos do Db2 para reconstruir o SQL responsável pela modificação dos dados perdidos e re-executá-lo. Adicionou-se esta capacidade e testou-se. O Agente de IA consegui recuperar todos os dados perdidos a partir do log do Db2.


A falha injetada

O incidente foi injetado com o seguinte 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"

Tudo foi desenhado intencionalmente de forma a obter um incidente simples de drop de tabela:

  • Uma única tabela removida, sem chaves externas ou vistas dependentes que complicassem o diagnóstico.

À primeira vista, parecia o tipo de caso que um playbook convencional de recuperação deveria conseguir resolver bem.


O pedido ao agente

Depois da injeção, o utilizador perguntou:

Parece que algumas tabelas foram eliminadas. Investigue, crie um plano e execute o plano para efetuar a recuperação.

Importa analisar a formulação porque o pedido não indica ao agente:

  • Qual a tabela eliminada
  • Como ocorreu a eliminação
  • Qual o caminho de recuperação a seguir

O agente teve de investigar, formar um plano com base na evidência e só depois executar a recuperação. Por outras palavras, tratou-se de uma tarefa de raciocínio, não de uma simples execução de comandos.


O que o agente encontrou primeiro

A primeira descoberta útil estava no rasto deixado pela própria injeção:

  • A tabela STORE.AUDIT_LOG continha o marcador do incidente
  • O marcador identificava STORE.PAYMENTS
  • A descrição confirmava o incidente DL-01

O significado e relevância desta tabela foram inferidos pelo agente. A partir daí, o agente verificou que:

  • A tabela STORE.PAYMENTS já não existia, tendo desaparecido a informação esperada no catálogo do Db2
  • Os outros objetos do schema STORE continuavam presentes

Isto levou à primeira conclusão importante: Não se tratava de uma falha ao nível do schema inteiro mas sim de uma perda de um objeto específico. A investigação focou-se então neste objecto:

O agente resumiu então a situação da seguinte forma:

ItemDetalhe
Tabela eliminadaSTORE.PAYMENTS
Número conhecido de linhas antes da perda19 linhas
Momento do drop2026-05-20 20:35:40
Backup disponívelBackup completo em /database/backup
Momento do backup20:19:51, antes da criação do schema STORE
Archive loggingLOGARCHMETH1=LOGRETAIN
Localização dos logs ativos/database/data/db2inst1/NODE0000/SQL00001/LOGSTREAM0000/
Sintoma imediatoSQL0204N ao aceder a STORE.PAYMENTS
Evidência adicionalMarcador no AUDIT_LOG, ausência de entradas de catálogo para a tabela e os seus índices

O primeiro caminho de recuperação parecia promissor

A pergunta seguinte era se a tabela podia ser recuperada através do mecanismo normal de restore e rollforward do Db2.

O ambiente tinha um backup completo em /database/backup e archive logging ativo com LOGARCHMETH1=LOGRETAIN

Nessa altura, a situação parecia favorável. Um padrão comum de recuperação seria:

PassoAção de recuperação comumObjetivo
1Restaurar a base de dados para uma base de recuperação separadaTrabalhar sobre uma cópia segura em vez do sistema live
2Fazer rollforward até um ponto imediatamente anterior ao dropRecuperar o estado do objeto antes do incidente
3Exportar a tabela recuperadaExtrair os dados recuperados da base lateral
4Recriar ou importar de volta para a base liveRepor o objeto recuperado em produção

Esta é uma abordagem técnica comum e perfeitamente razoável. Depois disso, o caso ficou mais interessante.


A primeira complicação: o backup era demasiado antigo

O backup existia, mas tinha sido feito antes de todo o schema STORE ter sido criado. O restore desse backup não devolveria por si a tabela STORE.PAYMENTS. O único caminho viável passava a depender do rollforward dos logs.

Mesmo assim, ainda parecia que a tabela e os dados eram recuperáveis porque:

  • O archive logging estava ativo
  • Os logs necessários para aplicar as modificações posteriores ao backup tinham sido mantidos

O plano construído pelo agente

Face a essa evidência, o agente elaborou o seguinte plano:

PassoAção planeadaResultado pretendido
1Restaurar o backup antigo para uma base de recuperação separadaEvitar tocar na base live durante a investigação
2Fazer rollforward dessa base para imediatamente antes do dropRecriar o estado pré-incidente se a granularidade temporal do Db2 o permitisse
3Recuperar aí a definição e os dados da tabelaUsar a cópia de recuperação como fonte de extração
4Transferir o objeto recuperado para a base liveRestabelecer o serviço em LABDB
5Validar integridade e limparDeixar a base de produção num estado fiável

Isto já era um raciocínio sólido de DBA e não fora as limitações do Db2 em termos de recuperação para ponto no tempo, a história terminaria aí.

Mas não terminou.


O verdadeiro problema no Db2: o PITR não conseguia isolar as linhas

O agente restaurou o backup para uma base de dados de recuperação separada e aplicou rollforward.

O que encontrou foi uma dificuldade subtil:

  • O DDL de CREATE TABLE STORE.PAYMENTS era recuperável
  • No entanto, as linhas da tabela não eram recuperáveis

Porquê?

Porque o preenchimento da tabela e o drop foram ambos confirmados (commited) no mesmo segundo.

A linha do tempo relevante era:

HoraEventoSignificado
20:32:09CREATE TABLE PAYMENTSA definição da tabela entra nos logs muito antes do segundo crítico
20:35:40.xxxINSERT INTO PAYMENTS (...)As 19 linhas de pagamentos são confirmadas
20:35:40.995DROP TABLE PAYMENTSA tabela é eliminada no mesmo segundo do insert

O rollforward point-in-time do Db2 aceita timestamps com granularidade mínima de um segundo.

Isso significa que as opções reais de recuperação eram:

Alvo de rollforwardResultado
20:35:39Tudo o que foi confirmado até 20:35:39; as linhas ainda não existem
20:35:40Tudo o que foi confirmado até 20:35:40; inclui o INSERT e o DROP
fim dos logsO mesmo resultado prático que 20:35:40; o drop continua a prevalecer

Não existia nenhum alvo válido que incluísse as linhas inseridas mas excluísse o drop e aqui está o verdadeiro coração técnico do caso.

A falha já não era:

  • “Como recuperar uma tabela eliminada?”

Passava a ser:

  • “Como recuperar quando a recuperação física point-in-time consegue devolver a estrutura mas já não o conjunto original de linhas?”

Esse é um problema muito mais difícil.


O que o Db2 ainda conseguiu recuperar

Mesmo sem conseguir isolar as linhas, a base de dados de recuperação ainda deu ao agente algo muito valioso:

  • A definição exata da tabela
  • A chave primária
  • A chave externa
  • Os check constraints
  • A definição do índice existente sobre a tabela

O agente extraiu essa definição com db2look e recriou a tabela na base de dados real. Após isso, a estrutura da tabela estava recuperada mas os dados estavam em falta.

O agente não parou. Ele não desistiu e foi mais longe. O registo de execução até aí podia ser resumido assim:

PassoAçãoResultado
1Backup completo identificado em /database/backupBackup válido encontrado, mas demasiado antigo para recuperação direta do objeto
2LOGARCHMETH1=LOGRETAIN confirmadoO caminho de recuperação por logs mantinha-se viável
3Logs arquivados e ativos consolidadosA base de recuperação passou a ter o material necessário para PITR
4Redirected restore para LABRCVRSucesso
5Rollforward para imediatamente antes do dropDDL recuperável, dados ainda ausentes
6db2look sobre LABRCVRDDL exato extraído, incluindo PK, FK, checks e índice
7Tabela recriada em LABDB liveEstrutura reposta com exatidão

O passo decisivo: reconstrução dos dados recorrendo a lógica de negócio

Esta é a parte mais importante do artigo. O agente mudou a abordagem de “raciocínio sobre como efetuar a recuperação física” para “uma vez que a recuperação física não é possível, como efetuar a reconstrução dos dados recorrendo a lógica do negócio”.

Analisou o contexto transacional sobrevivente em torno de STORE.PAYMENTS, em especial STORE.ORDERS, e inferiu quais as encomendas que logicamente deveriam ter pagamentos correspondentes.

A interpretação central foi:

  • Encomendas DELIVERED devem já ter pagamentos válidos
  • Encomendas SHIPPED também devem ter pagamentos
  • Encomendas PROCESSING, PENDING e CANCELLED não devem ter pagamentos

Isto produziu:

Estado da encomendaEncomendasLinhas de pagamento reconstruídas
DELIVERED1–12, 31–3315
SHIPPED13–164
PROCESSING17–20, 36–370
PENDING21–26, 38–400
CANCELLED27–300

Total:

  • 15 + 4 = 19

O valor coincidia exatamente com o número conhecido de linhas perdidas.

O agente reconstruiu então:

  • AMOUNT a partir de STORE.ORDERS.TOTAL_AMOUNT
  • Datas de pagamento plausíveis em relação às datas das encomendas
  • Métodos de pagamento permitidos
  • Valores válidos para o estado do pagamento
  • Referências sintaticamente plausíveis

Em suma:

  • A recuperação Db2 devolveu a definição da tabela
  • O agente reconstruiu um conjunto de linhas consistente com o negócio

Uma ferramenta não baseada em IA, em regra, não conseguiria fazer sozinha.

É importante ter em atenção que o Agente, fazendo o melhor que podia, não consegue fazer milagres. A reconstrução foi uma medida de recurso e não pode ser comparada com recuperação exata e completa dos dados. Os valores originais de algumas colunas perderam-se definitivamente, incluindo:

  • Timestamps exatos dos pagamentos
  • Métodos de pagamento originais por encomenda
  • Identificadores de referência originais

Os detalhes da recuperação e reconstrução são apresentados abaixo:

AspetoRecuperação exata?Observações
Definição da tabelaSimRecuperada dos logs e extraída com db2look
Chave primáriaSimRecriada exatamente
Chave externaSimRecriada exatamente
Check constraintsSimRecriados exatamente
Definição do índiceSimRecriada exatamente
Montantes originais dos pagamentosNa prática, simDerivados diretamente de STORE.ORDERS.TOTAL_AMOUNT
Timestamps originais dos pagamentosNãoReconstruídos como valores plausíveis
Métodos originais de pagamentoNãoReconstruídos dentro dos valores permitidos
Referências originais de pagamentoNãoReconstruídas como identificadores plausíveis
Identidade e número original de linhasParcialmenteA contagem coincidiu exatamente; o conteúdo foi fiel ao negócio, não original

Validação após a reconstrução

O agente não parou depois de inserir 19 linhas plausíveis. Validou que:

  • A tabela recriada correspondia ao DDL original
  • As relações de chave externa continuavam válidas
  • Não existiam referências órfãs
  • As estatísticas tinham sido refrescadas com RUNSTATS

O estado final ficou assim:

  • STORE.PAYMENTS voltou a existir
  • 19 linhas foram repostas em forma consistente com o negócio
  • A integridade referencial ficou limpa
  • A recuperação foi registada em STORE.AUDIT_LOG

Do ponto de vista operacional, a tabela voltou a ser utilizável.

O resumo final da recuperação pode ser apresentado assim:

PassoAçãoResultado
1Redirected restore e PITR para LABRCVRDefinição da tabela recuperada, linhas ainda em falta
2Extração com db2look a partir de LABRCVRDDL exato capturado
3Tabela recriada em LABDB liveEstrutura de produção reposta
419 linhas reconstruídas a partir do padrão de negócio em STORE.ORDERSDados em falta repostos de forma semanticamente consistente
5Validação de FK e verificações de integridadeSem linhas órfãs
6Execução de RUNSTATSEstatísticas do otimizador refrescadas
7Registo da recuperação e limpeza da base temporáriaIncidente encerrado com rastreabilidade

Uma abordagem técnica melhor teria sido a análise forense dos logs

Embora a reconstrução tenha tido sucesso e tenha interesse didático, esse não seria um caminho a percorrer normalmente.

O melhor caminho teria sido a análise forense dos logs do Db2, porque isso teria permitido ao agente extrair os valores originais das linhas inseridas em vez de os reconstruir com base na lógica de negócio.

Na altura do incidente, essa opção não estava disponível através dos servidores MCP. As ferramentas expostas pelo servidor suportavam investigação, restore, rollforward, inspeção de catálogo e extração de DDL, mas ainda não expunham utilitários forenses de leitura dos logs brutos.

Essa limitação foi entretanto corrigida.

O db2fmtlog pode ajudar a formatar e inspecionar conteúdo de baixo nível dos logs durante a investigação forense.

As capacidades que foram adicionadas:

FerramentaObjetivo
forensic_log_formatEncapsula db2fmtlog para formatar conteúdo de baixo nível dos logs de modo a que possa ser inspecionado e consumido pelo workflow de recuperação
recover_dropped_tableOrquestra tanto o caminho standard RESTORE → ROLLFORWARD → EXPORT como o caminho forense quando é detetada uma colisão no mesmo segundo

Para este incidente na tabela PAYMENTS, o workflow melhorado teria funcionado da seguinte maneira:

PassoAção do workflow forenseObjetivo
1Detectar que o INSERT e o DROP colidiram dentro do mesmo segundoReconhecer que o PITR não consegue isolar as linhas pretendidas
2Recolher a janela exata de logs e os metadados do objetoDelimitar a investigação forense à evidência relevante
3Efetuar uma análise forense real de baixo nível sobre essa janelaRecuperar os valores originais inseridos, se a ferramenta subjacente, incluindo inspeção formatada com db2fmtlog, o permitir
4Descodificar os valores recuperados com base no DDL de PAYMENTSMapear novamente os valores para colunas e tipos Db2
5Reconstruir instruções INSERT exatas com os valores originaisRecuperar as linhas reais em vez de as aproximar

O resultado esperado seria:

EtapaResultado forense
Classificação do incidenteColisão no mesmo segundo detetada e sinalizada como não resolúvel apenas por PITR
Extração forenseValores originais das linhas recuperados, se o caminho de análise de baixo nível os suportar
Descodificação guiada por DDLColunas mapeadas de novo para PAYMENT_ID, ORDER_ID, PAYMENT_DATE, AMOUNT, METHOD, STATUS e REFERENCE
Recuperação finalINSERT exatos reconstruídos com os timestamps, métodos e referências originais
  • A lógica standard de recuperação Db2 continua a ser o primeiro caminho
  • A investigação forense baseada no db2fmtlog passa a ser o fallback quando o PITR do Db2 não consegue isolar as linhas pretendidas

Com esta adição, o agente deixará de ter de parar na reconstrução fiel ao negócio em cenários de colisão no mesmo segundo. Poderá tentar primeiro a recuperação exata das linhas e só recorrer à reconstrução se a extração forense também falhar.


O que aconteceu depois da melhoria do MCP

Depois da melhoria do MCP server, o incidente foi revisitado e o agente conseguiu recuperar a tabela com as 19 linhas originais exatas, em vez de apenas com uma aproximação baseada na análise de dados de negócio.

Esta segunda recuperação é importante porque mostra a diferença entre:

  • Uma reconstrução estruturalmente correta
  • E uma recuperação verdadeira dos valores originais das linhas

O pós-mortem pode ser resumido assim:

TópicoO que aconteceu
IncidenteSTORE.PAYMENTS foi eliminada em 2026-05-20 20:35:40
Resultado inicialA tabela foi reconstruída com 19 linhas aproximadas com base na lógica de negócio
Problema remanescenteAlguns valores reconstruídos estavam errados, em especial a linha 13 e as linhas 17–19
Limitação de baseO PITR continuava sem conseguir isolar o INSERT do DROP, porque ambos ocorreram no mesmo segundo
Caminho melhoradoFoi usada análise do conteúdo dos logs para recuperar os valores originais

1. Identificar o ficheiro de log correto

O primeiro problema prático foi a escala dos dados:

  • Existiam 26 ficheiros de log entre localizações ativas e arquivadas
  • Uma passagem forense sem filtros produzia demasiado output

O agente usou as secções XHDR da saída de db2fmtlog para mapear intervalos de LFS para ficheiros físicos. Os registos INSERT de interesse tinham:

  • LFS = 6976

Isso permitiu mapear a atividade relevante para:

  • S0000007.LOG

que se revelou ser um ficheiro do log ativo.

2. Localizar exatamente os 19 registos INSERT

O passo seguinte foi encontrar os offsets exatos em bytes das 19 linhas inseridas dentro de S0000007.LOG.

O db2fmtlog forneceu a meta-informação necessária para orientar a pesquisa:

  • Transaction ID
  • Tipo de registo
  • Identidade do objeto

A pesquisa binária passou então a procurar a combinação de:

  • Os bytes de TID da transação 000000000C6D
  • O marcador INSREC_DP associado ao objeto de destino

Isto permitiu localizar os 19 registos nos offsets exatos dentro do ficheiro de log de 16 KB.

3. Descodificar corretamente o formato da linha

Este foi o passo mais difícil, porque o formato interno da linha no Db2 não estava documentado de forma amigável.

Foi necessário corrigir vários erros de descodificação:

Sub-problemaErro inicialCorreção final
Offset de início da linhaOs primeiros offsets produziam lixoO início correto da linha era pos + 32
Descodificação de DECIMAL(12,2)O packed BCD foi lido com alinhamento de nibble erradoO primeiro nibble era padding e tinha de ser ignorado
Colunas de comprimento variávelA estrutura foi inicialmente assumida como tendo uma tabela de offsets no fimA linha real usava campos explícitos de 2 bytes com comprimentos antes do texto
Timestamp da linha 13Os bytes onde se esperava o timestamp revelaram formato BCD inválidoA linha 13 continha 20 bytes extra de overhead de log antes dos dados de coluna

Depois dessas correções, o agente conseguiu descodificar corretamente os valores originais.

4. Aplicar os valores exatos de volta à tabela live

Ainda foi preciso ultrapassar dois problemas operacionais:

ProblemaResolução
A ferramenta execute_query permitia apenas instruções SELECTO DML foi aplicado por docker exec e pelo CLP do Db2
Eram necessários INSERT explícitos numa coluna identity GENERATED ALWAYSPAYMENT_ID foi alterada temporariamente para GENERATED BY DEFAULT e depois restaurada para GENERATED ALWAYS

O caminho final de inserção usou um ficheiro de script CLP com db2 -tvf, porque o quoting direto de INSERT multi-linha através de docker exec ... bash -c se tornou demasiado frágil.

O que a primeira reconstrução tinha assumido erradamente

A reconstrução inicial por lógica de negócio foi suficiente para repor o serviço, mas não era exata.

A recuperação forense posterior mostrou claramente as diferenças:

LinhaCampoReconstruídoReal
13PAYMENT_DATE09:0017:00
13AMOUNT99.98179.99
17ORDER_ID1731
18ORDER_ID1832
19ORDER_ID1933

A primeira reconstrução tinha acertado:

  • Os padrões dos métodos
  • Os valores de estado
  • A estrutura das referências

Mas continuava a falhar em valores originais exatos, em pontos onde só uma análise real de baixo nível aos logs podia chegar.

Estado final depois da recuperação melhorada

Depois da melhoria do MCP e do novo caminho de recuperação:

  • STORE.PAYMENTS voltou a conter 19 linhas
  • As linhas correspondiam aos valores originais exatos existentes antes do drop
  • O comportamento da identity foi restaurado para GENERATED ALWAYS
  • Não foi necessário qualquer restore de backup para a etapa final de recuperação exata dos dados

Isto torna a lição principal do caso ainda mais clara:

  • A reconstrução por lógica de negócio já era valiosa
  • Mas, depois da melhoria do MCP, o agente conseguiu ir além da aproximação e recuperar exatamente o conjunto original de linhas

Porque é que este caso importa

Quando a recuperação convencional encontrou um limite real do Db2, o agente de IA conseguiu continuar raciocinando sobre a semântica do negócio em vez de parar na fronteira técnica

Isto exigiu três níveis distintos de capacidade:

1. Ferramentas de investigação Db2

  • Verificações de catálogo
  • Inspeção de configuração
  • Compreensão de backups e logs
  • Execução de recuperação

2. Conhecimento de recuperação em Db2

  • Redirected restore
  • Limitações do rollforward
  • Extração de DDL
  • Passos de validação

3. Inferência por lógica de negócio

  • Compreender o que representa uma tabela de pagamentos
  • Inferir que encomendas deveriam ter pagamentos
  • Reconstruir linhas coerentes com a evidência de negócio que sobreviveu

As ferramentas tradicionais são fortes nos dois primeiros níveis.

O valor distintivo da IA apareceu no terceiro. O LLM usado foi o Claude Sonnet 4.6 da Anthropic.


Referências

Mais nesta área

Mais nesta área

Voltar à formação
Categoria de artigos

Artigos de Db2 LUW

Veja todos os artigos técnicos de Db2 LUW numa única página de categoria.

Abrir categoria Db2 LUW