Para melhor visualização, recomendo resolução de no mínimo 1024 x 768 e navegador Mozilla Firefox


segunda-feira, 1 de fevereiro de 2016

RMAN - Recuperando blocos de dados corrompidos (Block Media Recovery)

Por Eduardo Legatti

Olá,

Com o RMAN (Recovery Manager) é possível recuperar blocos de dados que foram corrompidos por algum motivo, seja por falhas de hardware, sistema operacional, ou até mesmo por problemas na própria instância do Oracle. Sempre que o erro "ORA-01578: bloco de dados ORACLE danificado (arquivo núm. string, bloco núm. string)" é reportado, significa que o Oracle tentou acessar algum bloco de dados corrompido em um arquivo de dados (datafile). Quando isso acontece, o DBA precisa identificar os blocos que foram corrompidos, a natureza da corrupção, e analisar as opções disponíveis para resolver o problema. Para um melhor entendimento sobre o conceito de tablespaces, segmentos (segments), extensões (extents) e blocos (blocks), acesse o artigo de Março/2008.

Quais opções temos para resolver o problema e suspender a emissão do erro ORA-01578?
  • Se for identificado que o segmento afetado é um índice de tabela, recriá-lo resolverá o problema.
  • Caso seja outro segmento como uma tabela, verificar se existe uma cópia da tabela em outro lugar de forma que seja possível reconstruir as linhas afetadas.
  • Executar CREATE TABLE...AS SELECT na tabela identificada com blocos corrompidos de forma a isolar as linhas saudáveis das linhas corrompidas.
  • Ignorar os blocos corrompidos fazendo uso de algumas procedures da package DBMS_REPAIR. (FIX_CORRUPT_BLOCKS e SKIP_CORRUPT_BLOCKS).
  • Realizar uma recuperação do bloco corrompido (Block Media Recovery).
Dentre as opções citadas acima, irei tratar neste artigo a recuperação física do bloco corrompido utilizando a técnica "Block Media Recovery" do RMAN. Para tanto, um backup físico do banco de dados, ou até mesmo um backup físico da tablespace afetada pela corrupção do bloco deverá estar disponível. Vale a pena salientar que o backup a ser utilizado deverá um que foi criado antes do bloco ter sido corrompido. Para realizar uma simulação, irei executar primeiro um backup da tablespace TBS_DATA_01, depois irei corromper alguns blocos no arquivo de dados pertencente a tablespace em questão, e por fim realizar a recuperação dos blocos corrompidos usando o RMAN através do comando BLOCKRECOVER.

[oracle@server01 ~]$ rman target /

Recovery Manager: Release 11.2.0.3.0 - Production on Mon Feb 1 10:27:33 2016

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

connected to target database: BD01 (DBID=3046620119)

RMAN> backup tablespace TBS_DATA_01;

Starting backup at 01/02/2016
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=6 device type=DISK
channel ORA_DISK_1: starting compressed full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
input datafile file number=00058 name=/data/oracle/BD01/TBS_DATA_01.dbf
channel ORA_DISK_1: starting piece 1 at 01/02/2016
channel ORA_DISK_1: finished piece 1 at 01/02/2016
piece handle=/backup/flash_recovery_area/BD01/backupset/2016_02_01/o1_mf_nnndf_TAG20160105T144408_c8qwps0q_.bkp tag=TAG20160105T144408 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 01/02/2016

Após a realização do backup da tablespace TBS_DATA_01, alvo de blocos de dados que irei corromper mais a frente, segue abaixo a tabela que possivelmente será vítima destes blocos corrompidos. A tabela T1 está alocada na tablespace TBS_DATA_01, no arquivo de dados número 58, e possui atualmente 13 MB de tamanho com cerca de 1 milhão de linhas.
 
SQL> select segment_name,
  2         tablespace_name,
  3         header_file,
  4         header_block,
  5         blocks,
  6         bytes
  7    from dba_segments
  8   where segment_name = 'T1';

SEGMENT_NAME    TABLESPACE_NAME   HEADER_FILE HEADER_BLOCK     BLOCKS      BYTES
--------------- ----------------- ----------- ------------ ---------- ----------
T1              TBS_DATA_01                58          130       1664   13631488

1 linha selecionada.

SQL> select count(*) from T1;

  COUNT(*)
----------
   1000000

1 linha selecionada.

Irei executar o comando ANALYZE para validar e certificar que o segmento T1 está íntegro e não possui qualquer problema de corrupção em seus blocos de dados. Se nenhuma mensagem de erro for emitida é porque a tabela está íntegra e consistente.
 
SQL> analyze table T1 validate structure;

Tabela analisada.

Agora irei corromper alguns blocos do arquivo de dados TBS_DATA_01.dbf. Como o bloco de cabeçalho do datafile é o 130, irei corromper os seguintes blocos acima dele: 140, 240, 340 e 440 conforme demonstrado abaixo.
 
[oracle@server01 ~]$ dd if=/dev/zero of=TBS_DATA_01.dbf bs=8192 seek=140 conv=notrunc count=1
1+0 records in
1+0 records out
8192 bytes (8.2 kB) copied, 4.9809e-05 s, 164 MB/s

[oracle@server01 ~]$ dd if=/dev/zero of=TBS_DATA_01.dbf bs=8192 seek=240 conv=notrunc count=1
1+0 records in
1+0 records out
8192 bytes (8.2 kB) copied, 4.5978e-05 s, 178 MB/s

[oracle@server01 ~]$ dd if=/dev/zero of=TBS_DATA_01.dbf bs=8192 seek=340 conv=notrunc count=1
1+0 records in
1+0 records out
8192 bytes (8.2 kB) copied, 4.3908e-05 s, 187 MB/s

[oracle@server01 ~]$ dd if=/dev/zero of=TBS_DATA_01.dbf bs=8192 seek=440 conv=notrunc count=1
1+0 records in
1+0 records out
8192 bytes (8.2 kB) copied, 4.3342e-05 s, 189 MB/s

Para garantir que os dados serão lidos diretamente do arquivo de dados TBS_DATA_01.dbf e não do buffer cache na SGA, irei forçar a limpeza do cache e realizar a execução de uma instrução SELECT na tabela T1.
 
SQL> alter system flush buffer_cache;

Sistema alterado.

Veremos abaixo que tanto a instrução SELECT quanto o comando ANALYZE irão falhar e emitir o erro ORA-01578. Percebe-se que a mensagem de erro virá acompanhada do número do arquivo de dados e do número do bloco afetado. Neste caso, (arquivo núm. 58, bloco núm. 140).
 
SQL> select count(*) from T1;
select count(*) from T1
*
ERRO na linha 1:
ORA-01578: bloco de dados ORACLE danificado (arquivo núm. 58, bloco núm. 140)
ORA-01110: 58 do arquivo de dados: '/data/oracle/BD01/TBS_DATA_01.dbf'

SQL> analyze table T1 validate structure;
analyze table T1 validate structure
*
ERRO na linha 1:
ORA-01578: bloco de dados ORACLE danificado (arquivo núm. 58, bloco núm. 140)
ORA-01110: 58 do arquivo de dados: '/data/oracle/BD01/TBS_DATA_01.dbf'

Para identificar qual objeto foi afetado pela corrupção do bloco, bastará executar o SQL abaixo que seleciona dados da view de dicionários de dados DBA_EXTENTS. Como demonstrado pelo resultado abaixo, foi comprovado que o segmento afetado pelos blocos corrompidos foi a tabela T1.
 
SQL> select segment_type,owner,segment_name from dba_extents
  2  where file_id = 58 and 131 between block_id
  3  and block_id+blocks -1;

SEGMENT_TYPE       OWNER                          SEGMENT_NAME
------------------ ------------------------------ ------------------------------
TABLE              SCOTT                          T1

1 linha selecionada.

Existem outras formas de diagnosticar e coletar maiores informações sobre blocos que foram corrompidos nos arquivos de dados do Oracle. Dentre elas posso citar a utilização do utilitário DBVERIFY e do próprio comando VALIDATE do utilitário RMAN. Para usar o DBVERIFY, irei me certificar do tamanho padrão do bloco utilizado pela tablespace TBS_DATA_01.
 
SQL> select tablespace_name,block_size
  2    from dba_tablespaces
  3   where tablespace_name = 'TBS_DATA_01';

TABLESPACE_NAME                BLOCK_SIZE
------------------------------ ----------
TBS_DATA_01                          8192

Abaixo irei fazer uma análise no arquivo de dados TBS_DATA_01.dbf. Para tanto, após entrar no diretório onde está localizado o arquivo de dados, executarei o DBVERIFY através do utilitário dbv.
 
[oracle@server01 ~]$ dbv blocksize=8192 file=TBS_DATA_01.dbf feedback=1000

DBVERIFY: Release 11.2.0.3.0 - Production on Mon Feb 1 10:49:42 2016

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

DBVERIFY - Verification starting : FILE = /data/oracle/BD01/TBS_DATA_01.dbf
Page 140 is marked corrupt
Corrupt block relative dba: 0x0e80008c (file 58, block 140)
Completely zero block found during dbv:

Page 240 is marked corrupt
Corrupt block relative dba: 0x0e8000f0 (file 58, block 240)
Completely zero block found during dbv:

Page 340 is marked corrupt
Corrupt block relative dba: 0x0e800154 (file 58, block 340)
Completely zero block found during dbv:

Page 440 is marked corrupt
Corrupt block relative dba: 0x0e8001b8 (file 58, block 440)
Completely zero block found during dbv:

DBVERIFY - Verification complete

Total Pages Examined         : 1920
Total Pages Processed (Data) : 1520
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 0
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 160
Total Pages Processed (Seg)  : 0
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 236
Total Pages Marked Corrupt   : 4
Total Pages Influx           : 0
Total Pages Encrypted        : 0
Highest block SCN            : 2253598227 (1825.2253598227)

Acima é possível perceber que os blocos corrompidos foram identificados e prontamente reportados pelo utilitário. Utilizando o comando VALIDATE do RMAN para validar a tablespace TBS_DATA_01, veremos que também será reportado informações sobre blocos corrompidos.

RMAN> validate tablespace TBS_DATA_01;

Starting validate at 01/02/2016
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=6 device type=DISK
channel ORA_DISK_1: starting validation of datafile
channel ORA_DISK_1: specifying datafile(s) for validation
input datafile file number=00058 name=/data/oracle/BD01/TBS_DATA_01.dbf
channel ORA_DISK_1: validation complete, elapsed time: 00:00:01

List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
58   FAILED 0              236          1920            7840568913427
  File Name: /data/oracle/BD01/TBS_DATA_01.dbf
  Block Type Blocks Failing Blocks Processed
  ---------- -------------- ----------------
  Data       0              1520
  Index      0              0
  Other      4              164

validate found one or more corrupt blocks
See trace file /u01/app/oracle/diag/rdbms/bd01/BD01/trace/BD01_ora_11127.trc for details
Finished validate at 01/02/2016

A partir do Oracle 11g, quando uma instrução SQL é abortada devido a emissão do erro ORA-01578 pelo fato de a mesma ter tentado acessar algum bloco corrompido, o Oracle prontamente já carrega informações na view dinâmica de desempenho V$DATABASE_BLOCK_CORRUPTION com informações do bloco corrompido. No entanto, se eu quiser saber todos os blocos que estão corrompidos, preferencialmente executo o comando VALIDATE DATABASE no RMAN. Desta forma a view será carregada de uma única vez com todos os blocos corrompidos encontrados no banco de dados.
 
SQL> select * from v$database_block_corruption;

     FILE#     BLOCK#     BLOCKS CORRUPTION_CHANGE# CORRUPTION_TYPE
---------- ---------- ---------- ------------------ --------------------
        58        140          1                  0 ALL ZERO
        58        240          1                  0 ALL ZERO
        58        340          1                  0 ALL ZERO
        58        440          1                  0 ALL ZERO

4 linhas selecionadas.

No resultado acima, os blocos corrompidos estão listado na coluna BLOCK#. Segue abaixo uma consulta que eu criei que retorna informações adicionais sobre os blocos corrompidos. Essas informações incluem o nome do arquivo de dados, nome da tablespace, nome do objeto que foi afetado, entre outras. Para fins estéticos, eu omiti algumas colunas do resultado do SQL abaixo.
 
SQL> SELECT file#,
  2         file_name,
  3         c.tablespace_name,
  4         block#,
  5         corruption_change#,
  6         corruption_type,
  7         segment_type,
  8         a.owner,
  9         segment_name,
 10         partition_name,
 11         skip_corrupt
 12    FROM dba_extents a, V$DATABASE_BLOCK_CORRUPTION b, dba_data_files c, dba_tables d
 13   WHERE     b.file# = c.file_id
 14         AND a.file_id = b.file#
 15         AND a.segment_name=d.table_name
 16         AND b.block# BETWEEN a.block_id AND a.block_id + a.blocks - 1;

FILE#   FILE_NAME                              TABLESPACE_NAME    BLOCK#
------- -------------------------------------- --------------- ---------
     58 /data/oracle/BD01/TBS_DATA_01.dbf      TBS_DATA_01           140
     58 /data/oracle/BD01/TBS_DATA_01.dbf      TBS_DATA_01           240
     58 /data/oracle/BD01/TBS_DATA_01.dbf      TBS_DATA_01           340
     58 /data/oracle/BD01/TBS_DATA_01.dbf      TBS_DATA_01           440

Agora irei me certificar que realmente possuo um backup da tablespace TBS_DATA_01 conforme demonstrado abaixo.

RMAN> list backup of tablespace TBS_DATA_01;

using target database control file instead of recovery catalog

List of Backup Sets
===================

BS Key  Type LV Size       Device Type Elapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
4095    Full    1.99M      DISK        00:00:01     01/02/2016
        BP Key: 4095   Status: AVAILABLE  Compressed: YES  Tag: TAG20160105T144408
        Piece Name: /backup/flash_recovery_area/BD01/backupset/2016_02_01/o1_mf_nnndf_TAG20160105T144408_c8qwps0q_.bkp
  List of Datafiles in backup set 4095
  File LV Type Ckp SCN    Ckp Time   Name
  ---- -- ---- ---------- ---------- ----
  58      Full 7840568913504 01/02/2016 /data/oracle/BD01/TBS_DATA_01.dbf

Por fim, irei realizar um recovery do bloco 140 do arquivo de dados 58 fazendo uso do RMAN através do comando BLOCKRECOVER como demonstrado a seguir.

RMAN> blockrecover datafile 58 block 140;

Starting recover at 01/02/2016
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=223 device type=DISK

channel ORA_DISK_1: restoring block(s)
channel ORA_DISK_1: specifying block(s) to restore from backup set
restoring blocks of datafile 00058
channel ORA_DISK_1: reading from backup piece /backup/flash_recovery_area/BD01/backupset/2016_02_01/o1_mf_nnndf_TAG20160105T144408_c8qwps0q_.bkp
channel ORA_DISK_1: piece handle=/backup/flash_recovery_area/BD01/backupset/2016_02_01/o1_mf_nnndf_TAG20160105T144408_c8qwps0q_.bkp tag=TAG20160105T144408
channel ORA_DISK_1: restored block(s) from backup piece 1
channel ORA_DISK_1: block restore complete, elapsed time: 00:00:01

starting media recovery
media recovery complete, elapsed time: 00:00:01

Finished recover at 01/02/2016

Ao acessar a view V$DATABASE_BLOCK_CORRUPTION, poderemos perceber que a linha referente ao bloco 140 desapareceu.
 
SQL> select * from v$database_block_corruption;

     FILE#     BLOCK#     BLOCKS CORRUPTION_CHANGE# CORRUPTION_TYPE
---------- ---------- ---------- ------------------ --------------------
        58        240          1                  0 ALL ZERO
        58        340          1                  0 ALL ZERO
        58        440          1                  0 ALL ZERO

3 linhas selecionadas.

Se quisermos recuperar todos os blocos listados na view V$DATABASE_BLOCK_CORRUPTION de uma única vez, bastará utilizar o comando abaixo.

RMAN> blockrecover corruption list;

Starting recover at 01/02/2016
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=223 device type=DISK

channel ORA_DISK_1: restoring block(s)
channel ORA_DISK_1: specifying block(s) to restore from backup set
restoring blocks of datafile 00058
channel ORA_DISK_1: reading from backup piece /backup/flash_recovery_area/BD01/backupset/2016_02_01/o1_mf_nnndf_TAG20160105T144408_c8qwps0q_.bkp
channel ORA_DISK_1: piece handle=/backup/flash_recovery_area/BD01/backupset/2016_02_01/o1_mf_nnndf_TAG20160105T144408_c8qwps0q_.bkp tag=TAG20160105T144408
channel ORA_DISK_1: restored block(s) from backup piece 1
channel ORA_DISK_1: block restore complete, elapsed time: 00:00:01

starting media recovery
media recovery complete, elapsed time: 00:00:01

Finished recover at 01/02/2016


Pronto. Os blocos foram recuperados e a instrução SELECT na tabela T1 irá executar sem problemas.
 
SQL> select * from v$database_block_corruption;

não há linhas selecionadas

SQL> select count(*) from T1;

  COUNT(*)
----------
   1000000

1 linha selecionada.

Google+

Nenhum comentário:

Postagens populares