Análise de um banco de dados corrompido

Olá,

Neste post farei a análise de um banco de dados corrompido. O banco de dados corrompido que irei utilizar foi disponibilizado num grupo de estudos para a certificação MCM no qual estou participando. O grupo foi iniciado pelo MVP Jason Strate ( blog | @StrateSQL ) e quem quiser mais informações pode acessar este post dele: http://www.jasonstrate.com/2011/12/sql-mcm-study-group/.

Vocês poderão baixar o banco de dados corrompido aqui.

Vamos começar a análise então. Antes de mais nada, precisamos deixar nosso banco de dados disponível em nossa instância. A maneira mais simples de fazer isto é anexar (attach) a base, seja pela interface gráfica ou via comando, desta forma:

CREATE DATABASE [CorruptDB1] ON
( FILENAME = N'C:\temp\CorruptDBs\CorruptDB1\DBFiles\CorruptDB1.mdf' ),
( FILENAME = N'C:\temp\CorruptDBs\CorruptDB1\DBFiles\CorruptDB1_log.ldf' )
FOR ATTACH;
GO

Substitua o caminho “C:\temp\CorruptDBs\…” para o local onde você escolher descompactar os arquivos.

Uma vez que o banco de dados estiver anexado, vamos rodar um DBCC CHECKDB para ver qual o estado do nosso banco.

DBCC CHECKDB(CorruptDB1)
WITH ALL_ERRORMSGS, NO_INFOMSGS

Utilizei as opções ALL_ERRORMSGS e NO_INFOMSGS para exibir como resultado somente mensagens de erro e ignorar mensagens informativas. Se desejar mais informações sobre o comando DBCC CHECKDB, clique aqui. O resultado que temos é o seguinte:

Msg 8939, Level 16, State 98, Line 1
Table error: Object ID 99, index ID 0, partition ID 0, alloc unit ID 6488064 (type System allocation data),
page (1:7). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 12716041 and -4.
CHECKDB found 1 allocation errors and 0 consistency errors in table ‘(Object ID 99)’ (object ID 99).
CHECKDB found 1 allocation errors and 0 consistency errors in database ‘CorruptDB1’.

Há algo errado com nosso banco de dados. E agora, que ação devemos tomar?

O comando CHECKDB possui duas opções chamadas REPAIR_ALLOW_DATA_LOSS e REPAIR_REBUILD. A primeira impressão é “…ótimo, o DBCC CHECKDB vai resolver meu problema de corrupção!”.

A primeira opção “tenta” reparar o problema de corrupção e pode ocasionar perda de dados. Já a segunda opção “tenta” executar reparos que não possibilitem perda de dados. Como não queremos perder nenhum dado da base, vamos executar a segunda opção primeiro. Vou alterar o acesso do banco de dados para SINGLE_USER e vou executar estas opções.

ALTER DATABASE CorruptDB1 SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO

DBCC CHECKDB(CorruptDB1, REPAIR_REBUILD)
WITH ALL_ERRORMSGS, NO_INFOMSGS

E temos o seguinte resultado:

Msg 8939, Level 16, State 98, Line 1
Table error: Object ID 99, index ID 0, partition ID 0, alloc unit ID 6488064 (type System allocation data), page (1:7). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 12584969 and -4.
The repair level on the DBCC statement caused this repair to be bypassed.
CHECKDB found 1 allocation errors and 0 consistency errors in table ‘(Object ID 99)’ (object ID 99).
CHECKDB found 1 allocation errors and 0 consistency errors in database ‘CorruptDB1’.

Hummm… Nada resolvido. Ainda tivemos o erro 8939. Vamos forçar a barra e tentar o reparo com possível perda de dados.

DBCC CHECKDB(CorruptDB1, REPAIR_ALLOW_DATA_LOSS)
WITH ALL_ERRORMSGS, NO_INFOMSGS

E o resultado:

Msg 8939, Level 16, State 98, Line 1
Table error: Object ID 99, index ID 0, partition ID 0, alloc unit ID 6488064 (type System allocation data), page (1:7). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 12584969 and -4.
The repair level on the DBCC statement caused this repair to be bypassed.
CHECKDB found 1 allocation errors and 0 consistency errors in table ‘(Object ID 99)’ (object ID 99).
CHECKDB found 1 allocation errors and 0 consistency errors in database ‘CorruptDB1’.

Novamente, nada feito. Pelo nível de corrupção do banco de dados, o comando DBCC CHECKDB não consegue reparar este problema. Vamos ter que fazer uma análise um pouco mais profunda e por a mão na massa para resolver o problema.

Nós vamos encontrar a explicação deste erro aqui. Em linhas gerais, ele nos informa que foi realizada uma validação numa determinada página, e esta validação falhou por uma corrupção no cabeçalho (Header) da página. Interessante não?!

Analisando a mensagem completa de erro que nos foi dada, podemos observar que ela nos informa qual é a página que está com problema: “…page (1:7). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. …”. Fica a dica: LEIA TODA A MENSAGEM DE ERRO!!! Achei que isto deveria ficar em destaque pois é muito comum ver as pessoas sem saber o que fazer e a mensagem de erro te dá dicas valiosas sobre o que aconteceu. Nem sempre é assim, mas na maioria das vezes é válido. Vamos dar uma olhada nesta página:

DBCC TRACEON(3604)
GO
DBCC PAGE('CorruptDB1', 1, 7, 3)

Quer saber mais sobre o Trace Flag 3604 e sobre o comando DBCC PAGE, acesse aqui e aqui.
O resultado completo que temos é o seguinte:

DBCC execution completed. If DBCC printed error messages, contact your system administrator.
Msg 8939, Level 16, State 98, Line 1
Table error: Object ID 99, index ID 0, partition ID 0, alloc unit ID 6488064 (type System allocation data), page (1:7). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 12584969 and -4.PAGE: (1:7)BUFFER:BUF @0x000000008EFA8F40

bpage = 0x000000008E17E000           bhash = 0x0000000000000000           bpageno = (1:7)
bdbid = 17                           breferences = 0                      bcputicks = 0
bsampleCount = 0                     bUse1 = 50557                        bstat = 0xc00809
blog = 0x21212159                    bnext = 0x0000000000000000

PAGE HEADER:

Page @0x000000008E17E000

m_pageId = (1:7)                     m_headerVersion = 0                  m_type = 0
m_typeFlagBits = 0x0                 m_level = 0                          m_flagBits = 0x200
m_objId (AllocUnitId.idObj) = 99     m_indexId (AllocUnitId.idInd) = 0    Metadata: AllocUnitId = 6488064
Metadata: PartitionId = 0            Metadata: IndexId = 0                Metadata: ObjectId = 99
m_prevPage = (0:0)                   m_nextPage = (0:0)                   pminlen = 90
m_slotCnt = 2                        m_freeCnt = 6                        m_freeData = 8182
m_reservedCnt = 0                    m_lsn = (0:0:1)                      m_xactReserved = 0
m_xdesId = (0:0)                     m_ghostRecCnt = 0                    m_tornBits = 105713478

Allocation Status

GAM (1:2) = ALLOCATED                SGAM (1:3) = NOT ALLOCATED           PFS (1:1) = 0x44 ALLOCATED 100_PCT_FULL
DIFF (1:6) = CHANGED                 ML (1:7) = NOT MIN_LOGGED
Msg 2514, Level 16, State 5, Line 1
A DBCC PAGE error has occurred: Invalid page type – dump style 3 not possible.

Obviamente houve erros no resultado. Mas o que chama a atenção são os pontos que destaquei em negrito. Se dermos uma olhada no Internals (um modo “carinhoso” de chamar o livro Microsoft SQL Server 2008 Internals), veremos no capítulo 3, página 147, parágrafo 2 ( :-O Não, eu não decorei isto, fui ver no livro… ^.^ ) as explicações sobre as páginas de alocação de dados, em resumo, assim:

  • Page 0 – File Header
  • Page 1 – PFS Page Free Space
  • Page 2 – GAM Global Allocation Map
  • Page 3 – SGAM Shared Global Allocation Map
  • Page 4 e Page 5 – não são utilizadas
  • Page 6 – DCM Differential Changed Map
  • Page 7 – BCM Bulk Changed Map

Isto nos leva à conclusão de que a página que temos corrompida neste banco de dados é a página BCM, Bulk Changed Map. Não vou entrar neste momento no detalhe das páginas de controle, ficando apenas por dizer que é uma das páginas de controle de alocação de dados do SQL Server.

Ótimo! Agora sabemos o que fazer. Podemos então fazer o restore desta página e tudo resolvido. Será?!
Vamos tentar.

USE MASTER;
GO

RESTORE DATABASE CorruptDB1
   PAGE = '1:7'
FROM DISK = 'C:\temp\CorruptDBs\CorruptDB1\Bak\CorruptDB1.bak'
WITH RECOVERY

Vamos utilizar o comando RESTORE para restaurar somente a nossa página corompida. O resultado que temos é:

Msg 3111, Level 16, State 1, Line 2
Page (1:7) is a control page which cannot be restored in isolation. To repair this page, the entire file must be restored.
Msg 3013, Level 16, State 1, Line 2
RESTORE DATABASE is terminating abnormally.

Ops! O SQL Server não nos permite restaurar isoladamente páginas de controle… E nos informa que para resolução do problema temos de restaurar o arquivo completo.

Solução para o problema e evitar perda de dados. Faça um backup de log (Tail Log Backups) e aplica um restore do banco de dados e depois aplique os logs na sequência, se houver, e por último aplique o último backup de log gerado (tail log). Depois de seguir estes passos, execute novamente o comando DBCC CHECKDB para ter certeza de que seu problema foi resolvido.

Esta foi a solução que encontrei para este problema de corrupção.

Bons estudos e até a próxima!

2 Respostas para “Análise de um banco de dados corrompido

  1. Erick, boa tarde.

    Excelente explicação. Tive esse problema em um dos meus clientes e restaurei o banco.

    Poderia por gentileza me dizer quais seriam as possíveis causas raiz para este tipo de problema?

    • Oi Chelder,

      Normalmente problemas de corrupção estão relacionados a problemas de hardware. Uma falha no disco ou ainda uma falha no momento de “escrever” o IO no disco e gerou um dado incorreto.

      Estas falhas levam à corrupção de dados.

      Dependendo de onde ocorre a falha, altera a gravidade do problema. Se a falha atingir páginas de controle do SQL (boot page, por exemplo) pode deixar a base indisponível e sem alternativa senão um restore para normalizar o ambiente.

      Obrigado pelo comentário.

      Um abraço,
      Erickson Ricci

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s