segunda-feira, 3 de dezembro de 2012

Migrando para o SecureFile LOBS no Oracle 11g com o DBMS_REDEFINITION

Olá,

No artigo de Novembro/2012 eu demonstrei um pouco sobre o Oracle SecureFile LOBS no Oracle 11g em relação à compressão de dados e deduplicação. Neste artigo irei demonstrar, de forma prática, uma das formas de como poderemos migrar uma coluna LOB, de BASICFILE, para SECUREFILE. Para isso, farei uso de um recurso que já foi  demonstrado no artigo de Abril/2011 que é a redefinição online de uma tabela (Online table redefintion) usando o pacote DBMS_REDEFINITION. Bom, até o momento, não existe uma forma automática ou um comando (ALTER TABLE ...) que altere uma coluna LOB (CLOB/BLOB) de BASICFILE para SECUREFILE. Apesar de podermos utilizar os métodos CTAS, Export/Import, ou até mesmo utilizar um comando UPDATE para atualizar os dados de uma coluna (BASICFILE) para uma nova coluna (SECUREFILE) na mesma tabela, na minha opinião, um ótimo método é fazer uso da redefinição online, pois o mesmo não ocasiona downtime para a aplicação que acessa o banco de dados, além de ser muito prático.

Abaixo, irei demonstrar na prática como poderemos migrar uma tabela que possui uma coluna LOB padrão BASICFILE, para uma coluna LOB SECUREFILE, já com a deduplicação e compressão média ativadas. A tabela original será a mesma utilizada no artigo de Novembro/2012, a diferença que o seu nome será BLOB_TABLE e não mais BLOB_BASIC.

Seguindo o roteiro já demonstrado no artigo de Abril/2011, irei iniciar o procedimento criando uma tabela interina (INTERIM) que será o objeto alvo da redefinição online. Note que a tabela será praticamente idêntica à tabela original, diferenciando-se apenas pela coluna BLOB, que será do tipo SECUREFILE com a deduplicação e compressão média ativadas. Em comparação com o artigo de Abril/2011, naquele artigo eu não tinha definido nenhuma constraint NOT NULL nas colunas da tabela INTERIM, afim de evitar qualquer tipo de problema nos procedimentos de redefinição online da tabela.

Para fins de didática, neste artigo irei definir as colunas como NOT NULL de forma a espelhar a tabela original. A diferença agora é que irei setar o parâmetro "ignore_errors" da procedure COPY_TABLE_DEPENDENTS como TRUE. Esta procedure pertence à package DBMS_REDEFINITION. Vale a pena salientar que o segmento LOB da tabela original possui cerca de 8,63 GB de tamanho e que, ao final da redefinição online, o mesmo terá cerca de 2,81 GB.
C:\ sqlplus system/manager

SQL*Plus: Release 11.2.0.3.0 Production on Seg Dez 3 19:57:53 2012

Copyright (c) 1982, 2010, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> create table scott.blob_table_interim
  2  (
  3    codigo    number(10) not null,
  4    nome      varchar2(255),
  5    documento blob
  6  )
  7  lob (documento) store as securefile
  8         (
  9           tablespace tbs_lob
 10           deduplicate
 11           compress medium
 12         )
 13  tablespace tbs_data;

Tabela criada.
Após a criação da tabela BLOB_TABLE_INTERIM acima, irei iniciar abaixo, o processo de redefinição online.

SQL> begin
  2  dbms_redefinition.start_redef_table
  3  (
  4     uname           => 'SCOTT'
  5   , orig_table      => 'BLOB_TABLE'
  6   , int_table       => 'BLOB_TABLE_INTERIM'
  7   , col_mapping     => null
  8   , options_flag    => dbms_redefinition.cons_use_pk
  9   , orderby_cols    => null
 10   , part_name       => null
 11  );
 12  end;
 13  /

Procedimento PL/SQL concluído com sucesso.

Decorrido: 00:21:09.42

Após a execução do processo acima, irei iniciar abaixo o processo que irá realizar a cópia dos objetos dependentes da tabela original para a tabela interina. Esse objetos incluem, por exemplo, contraints, triggers, valores default, etc...

SQL> var num_errors number
SQL> begin
  2  dbms_redefinition.copy_table_dependents
  3  (
  4      uname            => 'SCOTT'
  5    , orig_table       => 'BLOB_TABLE'
  6    , int_table        => 'BLOB_TABLE_INTERIM'
  7    , copy_indexes     => dbms_redefinition.cons_orig_params
  8    , copy_triggers    => true
  9    , copy_constraints => true
 10    , copy_privileges  => true
 11    , ignore_errors    => true
 12    , num_errors       => :num_errors
 13    , copy_statistics  => true
 14  );
 15  end;
 16  /

Procedimento PL/SQL concluído com sucesso.

Decorrido: 00:00:23.46

Após a execução do procedimento de cópia dos objetos dependentes, irei verificar o resultado da variável num_errors. Se a mesma tiver um valor diferente de zero, então é prudente verificar na view DBA_REDEFINITION_ERRORS, qual ação não foi realizada com sucesso.

SQL> print num_errors

NUM_ERRORS
----------
         1

SQL> select object_name, base_table_name, ddl_txt from dba_redefinition_errors;

OBJECT_NAME   BASE_TABLE_NAME     DDL_TXT
------------- ------------------- --------------------------------------------------
SYS_C003910   BLOB_TABLE          ALTER TABLE "SCOTT"."BLOB_TABLE_INTERIM" 
                                  MODIFY ("CODIGO" CONSTRAINT "TMP$$_SYS_C0039100"
                                  NOT NULL ENABLE NOVALIDATE)
1 linha selecionada.

Podemos verificar pelo resultado acima que o processo de cópia de objetos dependentes tentou modificar a coluna CODIGO da tabela interina para NOT NULL. Neste caso, poderemos ignorar esse erro. Abaixo está a instrução SQL na qual podemos verificar todos os objetos que foram redefinidos.

SQL> select object_type, object_name, base_table_name, interim_object_name
  2  from dba_redefinition_objects
  3  where base_table_name = 'BLOB_TABLE' order by 1,2,3;

OBJECT_TYPE  OBJECT_NAME         BASE_TABLE_NAME     INTERIM_OBJECT_NAME
------------ ------------------- ------------------- --------------------------
TABLE        BLOB_TABLE          BLOB_TABLE          BLOB_TABLE_INTERIM
CONSTRAINT   PK_BLOB_TABLE       BLOB_TABLE          TMP$$_PK_BLOB_TABLE0
INDEX        PK_BLOB_TABLE       BLOB_TABLE          TMP$$_PK_BLOB_TABLE0
INDEX        IDX_BLOB_TABLE_NOME BLOB_TABLE          TMP$$_IDX_BLOB_TABLE_NOME0

4 linhas selecionadas.

Bom, caso alguma linha da tabela original tenha sofrido algum tipo de alteração DML durante o processo de redefinição online, é prudente sincronizar a tabela interina antes de finalizar o processo de redefinição. Esta fase é importante para não sobrecarregar a fase de finalização "finish_redef_table".

SQL> begin
  2  dbms_redefinition.sync_interim_table
  3  (
  4      uname      => 'SCOTT'
  5    , orig_table => 'BLOB_TABLE'
  6    , int_table  => 'BLOB_TABLE_INTERIM'
  7    , part_name  => null
  8  );
  9  end;
 10  /

Procedimento PL/SQL concluído com sucesso.

Pronto. Agora poderemos finalizar o processo de redefinição online.

SQL> begin
  2  dbms_redefinition.finish_redef_table
  3  (
  4      uname      => 'SCOTT'
  5    , orig_table => 'BLOB_TABLE'
  6    , int_table  => 'BLOB_TABLE_INTERIM'
  7    , part_name  => null
  8  );
  9  end;
 10  /

Procedimento PL/SQL concluído com sucesso.

Vale a pena salientar que caso ocorra algum tipo de problema durante o processo de redefinição online ou apenas queiramos abortar o processo por algum motivo, poderemos utilizar a procedure "abort_redef_table" como demonstrado abaixo:

Caso queira abortar o processo
==============================

SQL> begin
  2  dbms_redefinition.abort_redef_table
  3  (
  4      uname=>'SCOTT'
  5    , orig_table=>'BLOB_TABLE'
  6    , int_table=>'BLOB_TABLE_INTERIM'
  7  );
  8  end;
  9  /

Procedimento PL/SQL concluído com sucesso.

Bom, após a execução de todas as operações acima, irei verificar o tamanho dos segmentos LOB do schema SCOTT utilizando uma consulta que eu criei e documentei conforme publicação do artigo de Maio/2011.

SQL> select table_name,
  2       decode(partitioned,'/','NO',partitioned) partitioned,
  3       num_rows,
  4       data_mb,
  5       indx_mb,
  6       lob_mb,
  7       total_mb
  8        from (select data.table_name,
  9                partitioning_type
 10               || decode (subpartitioning_type,
 11                          'none', null,
 12                          '/' || subpartitioning_type)
 13                      partitioned,
 14               num_rows,
 15               nvl(data_mb,0) data_mb,
 16               nvl(indx_mb,0) indx_mb,
 17               nvl(lob_mb,0) lob_mb,
 18               nvl(data_mb,0) + nvl(indx_mb,0) + nvl(lob_mb,0) total_mb
 19               from (  select table_name,
 20                     nvl(min(num_rows),0) num_rows,
 21                     round(sum(data_mb),2) data_mb
 22                        from (select table_name, num_rows, data_mb
 23                            from (select a.table_name,
 24                                  a.num_rows,
 25                                  b.bytes/1024/1024 as data_mb
 26                                    from user_tables a, user_segments b
 27                                   where a.table_name = b.segment_name))
 28                   group by table_name) data,
 29                   (  select a.table_name,
 30                          round(sum(b.bytes/1024/1024),2) as indx_mb
 31                       from user_indexes a, user_segments b
 32                         where a.index_name = b.segment_name
 33                      group by a.table_name) indx,
 34                   (  select a.table_name,
 35                         round(sum(b.bytes/1024/1024),2) as lob_mb
 36                      from user_lobs a, user_segments b
 37                     where a.segment_name = b.segment_name
 38                      group by a.table_name) lob,
 39                     user_part_tables part
 40               where     data.table_name = indx.table_name(+)
 41                     and data.table_name = lob.table_name(+)
 42                     and data.table_name = part.table_name(+))
 43    order by table_name;

TABLE_NAME          PARTITIONED NUM_ROWS  DATA_MB  INDX_MB   LOB_MB  TOTAL_MB
------------------- ----------- -------- -------- -------- -------- ---------
BLOB_TABLE          NO             15000      4,5      ,06     2880   2884,56
BLOB_TABLE_INTERIM  NO             15000      3,5        7     8841   8851,50

2 linhas selecionadas.

Podemos verificar que após o processo de redefinição online, a tabela original BLOB_TABLE foi renomeada para BLOB_TABLE_INTERIM e vice versa. Portanto, não precisaremos mais da tabela interina e poderemos dropá-la com segurança. Perceba a diferença de tamanho do segmento LOB após a redefinição online.

SQL> drop table blob_table_interim purge;

Tabela eliminada.

Por fim, mostrarei abaixo um teste em relação ao comando DML (INSERT), de forma a demonstrar a diferença de performance entre SECUREFILE e BASICFILE em uma coluna CLOB.

Oracle 10g
==========

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE    10.2.0.5.0      Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production

5 linhas selecionadas.

SQL> create table clob_basic
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  clob
  6  )
  7  lob (documento) store as (tablespace tbs_lob)
  8  tablespace tbs_data;

Tabela criada.

SQL> declare
  2    l_clob clob := rpad('x', 10000, 'x');
  3  begin
  4    for i in 1 .. 100000 loop
  5      insert into clob_basic values (i,'teste',l_clob);
  6    end loop;
  7    commit;
  8  end;
  9  /

Procedimento PL/SQL concluído com sucesso.

Decorrido: 00:10:46.94

Podemos verificar acima que, no Oracle 10g, o processo de inserção de 100.000 linhas em uma tabela com uma coluna CLOB (BASICFILE) demorou cerca de 10:46 minutos.

Oracle 11g
==========

SQL> select * from v$version;

BANNER
----------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0      Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

5 linhas selecionadas.

SQL> create table clob_basic
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  clob
  6  )
  7  lob (documento) store as (tablespace tbs_lob)
  8  tablespace tbs_data;

Tabela criada.

SQL> declare
  2    l_clob clob := rpad('x', 10000, 'x');
  3  begin
  4    for i in 1 .. 100000 loop
  5      insert into clob_basic values (i,'teste',l_clob);
  6    end loop;
  7    commit;
  8  end;
  9  /

Procedimento PL/SQL concluído com sucesso.

Decorrido: 00:01:19.60

Podemos verificar acima que, no Oracle 11g, o processo de inserção de 100.000 linhas em uma tabela com uma coluna CLOB (BASICFILE) demorou cerca de 01:19 minutos.

SQL> create table clob_securefile
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  clob
  6  )
  7  lob (documento) store as securefile (tablespace tbs_lob)
  8  tablespace tbs_data;

Tabela criada.

SQL> declare
  2    l_clob clob := rpad('x', 10000, 'x');
  3  begin
  4    for i in 1 .. 100000 loop
  5      insert into clob_securefile values (i,'teste',l_clob);
  6    end loop;
  7    commit;
  8  end;
  9  /

Procedimento PL/SQL concluído com sucesso.

Decorrido: 00:01:56.56

Podemos verificar acima que, no Oracle 11g, o processo de inserção de 100.000 linhas em uma tabela com uma coluna CLOB (SECUREFILE) demorou cerca de 01:56 minutos.

Portanto, de acordo com os resultados demonstrados pelos testes acima, podemos comprovar que realmente houve uma melhora considerável de performance (pelo menos no INSERT), comparando o Oracle 10g (BASICFILE) e o Oracle 11g (SECUREFILE). No entanto, podemos verificar que o BASICFILE no Oracle 11g não foi apenas superior ao BASICFILE do Oracle 10g, como também foi superior ao próprio SECUREFILE. Neste caso, concluo que o SECUREFILE é vantajoso quando utilizamos outras opções, sejam elas compressão, deduplicação ou criptografia.

sexta-feira, 2 de novembro de 2012

Um pouco sobre SecureFile LOBS no Oracle 11g


Olá,
No Oracle 11g, podemos escolher dois modos diferentes de criar e armazenar colunas LOB (Large Objects) no banco de dados. O modo BASICFILE é o único disponível até a versão do Oracle 10g. Com a versão do Oracle 11g temos, além do modo BASICFILE, o modo SECUREFILE, que trouxe várias melhorias em relação à performance (leitura/escrita), além de outras como deduplicação, compressão e criptografia para as colunas do tipo BLOB e CLOB.

O Oracle SecureFiles é uma infraestrutura de armazenamento totalmente nova que foi projetada para propiciar melhor desempenho e segurança, além de reduzir o uso de disco com os seus recursos avançados de compressão. O desempenho de leitura e escrita também é mais alto porque ele oferece uma nova camada de rede cliente/servidor permitindo uma transferência de dados mais veloz.

No caso da deduplicação, o Oracle é capaz de reduzir a redundância de dados atráves de um algoritmo interno de forma que LOBS idênticos são fundido em uma única imagem. Em relação à compressão de dados, temos a opção de compressão média (MEDIUM) e alta (HIGH). Em relação à criptografia, existem vários algoritmos que são suportados como 3DES168, AES128 ente outros. No mais, a intenção deste artigo será o de demonstrar e comparar o efeito da compressão de dados em colunas do tipo BLOB e CLOB.

No Oracle 11g, quando criamos uma coluna do tipo LOB (BLOB/CLOB), por padrão o mesmo será criado no antigo formato (BASICFILE). Se quisermos que o padrão seja SECUREFILE, deveremos alterar o valor do parâmetro db_securefile para ALWAYS ou FORCE. Seu valor default é PERMITTED. Vale a pena salientar que esse parâmetro pode ser alterado tanto no nível de sistema quanto no nível de sessão.

SQL> show parameter db_securefile

NAME                  TYPE        VALUE
--------------------- ----------- ---------
db_securefile         string      PERMITTED

Seguem abaixo os valores possíveis para o parâmetro db_securefile.
  • ALWAYS - Criam todos os LOBS como SecureFile, mesmo se BasicFile for especificado. Se a tablespace de destino não foi criada com o ASSM (Automatic Segment Space Management) habilitado, então um erro será emitido caso a palavra chave SECUREFILE não foi especificada.
  • FORCE - Criam todos os LOBS como SecureFile, mesmo se BasicFile for especificado. Um erro será gerado caso a tablespace não esteja com a ASSM habilitado.
  • PERMITTED - É o valor default e permite a criação de SecureFiles LOBS.
  • NEVER - Não será permitida a criação de SecureFile LOBs. Se a palavra chave SECUREFILE for usada, o LOB será criado como BasicFile. Um erro será emitido apenas se alguma opção (deduplicate, compress, encrypt) for utilizada.
  • IGNORE - A palavra chave SECUREFILE e todas opções serão ignoradas. Portanto, os LOBS serão criados como BasicFiles. 
Para fins de demonstração, irei criar 6 tabelas de forma a comparar o efeito da compressão de dados nos campos BLOB e CLOB. Segue abaixo o layout que utilizarei.
Nome da Tabela          Tipo de Dado   Tipo de LOB Compressão
----------------------- -------------- ----------- ----------
BLOB_BASIC              BLOB           BASICFILE   NENHUMA
BLOB_SECUREFILE_MEDIUM  BLOB           SECUREFILE  MÉDIA
BLOB_SECUREFILE_HIGH    BLOB           SECUREFILE  ALTA
CLOB_BASIC              CLOB           BASICFILE   NENHUMA
CLOB_SECUREFILE_MEDIUM  CLOB           SECUREFILE  MÉDIA
CLOB_SECUREFILE_HIGH    CLOB           SECUREFILE  ALTA
 
Abaixo irei criar as tabelas conforme o layout apresentado acima.
C:\>sqlplus scott/tiger

SQL*Plus: Release 11.2.0.3.0 Production on Sex Nov 2 12:57:53 2012

Copyright (c) 1982, 2010, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> create table blob_basic
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  blob
  6  )
  7  lob (documento) store as (tablespace tbs_lob)
  8  tablespace tbs_data;

Tabela criada.

SQL> create table blob_securefile_medium
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  blob
  6  )
  7  lob (documento) store as securefile (tablespace tbs_lob compress medium)
  8  tablespace tbs_data;

Tabela criada.

SQL> create table blob_securefile_high
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  blob
  6  )
  7  lob (documento) store as securefile (tablespace tbs_lob compress high)
  8  tablespace tbs_data;

Tabela criada.

SQL> create table clob_basic
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  clob
  6  )
  7  lob (documento) store as (tablespace tbs_lob)
  8  tablespace tbs_data;

Tabela criada.

SQL> create table clob_securefile_medium
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  clob
  6  )
  7  lob (documento) store as securefile (tablespace tbs_lob compress medium)
  8  tablespace tbs_data;

Tabela criada.

SQL> create table clob_securefile_high
  2  (
  3    id         number(10),
  4    nome       varchar2(255),
  5    documento  clob
  6  )
  7  lob (documento) store as securefile (tablespace tbs_lob compress high)
  8  tablespace tbs_data;

Tabela criada.


SQL> select table_name,column_name,segment_name,compression,securefile from user_lobs;

TABLE_NAME              COLUMN_NAME    SEGMENT_NAME              COMPRE SEC
----------------------- -------------- ------------------------- ------ ---
BLOB_BASIC              DOCUMENTO      SYS_LOB0000038106C00003$$ NONE   NO
BLOB_SECUREFILE_MEDIUM  DOCUMENTO      SYS_LOB0000038111C00003$$ MEDIUM YES
BLOB_SECUREFILE_HIGH    DOCUMENTO      SYS_LOB0000038114C00003$$ HIGH   YES
CLOB_BASIC              DOCUMENTO      SYS_LOB0000038117C00003$$ NONE   NO
CLOB_SECUREFILE_MEDIUM  DOCUMENTO      SYS_LOB0000038123C00003$$ MEDIUM YES
CLOB_SECUREFILE_HIGH    DOCUMENTO      SYS_LOB0000038120C00003$$ HIGH   YES

6 linhas selecionadas.
 
Com as tabelas criadas, irei popular as tabelas BLOB_BASIC e CLOB_BASIC de forma que as mesma fiquem com um tamanho de mais ou menos 8,6 GB. Apesar de eu não ter demonstrado a inserção dos dados nas tabelas, o conteúdo da coluna BLOB possui vários documentos binários diferentes (*.doc, *.xls, *.pdf). No caso da coluna CLOB, o seu conteúdo contém textos grandes com caracteres aleatórios. Sobre como carregar arquivos externos para tabelas do banco de dados, aconselho a leitura dos artigos de Dezembro/2009 e Janeiro/2010.

Bom, após o carregamento das tabelas BLOB_BASIC e CLOB_BASIC, irei carregar as demais tabelas utilizando o método abaixo:

insert into BLOB_SECUREFILE_MEDIUM select * from BLOB_BASIC;
insert into BLOB_SECUREFILE_HIGH   select * from BLOB_BASIC;
insert into CLOB_SECUREFILE_MEDIUM select * from CLOB_BASIC;
insert into CLOB_SECUREFILE_HIGH   select * from CLOB_BASIC;
 
Após a execução das operações DML acima, irei verificar o tamanho dos segmentos LOB utilizando uma consulta que eu criei e documentei conforme publicação do artigo de Maio/2011.
SQL>  select table_name,
  2        decode(partitioned,'/','NO',partitioned) partitioned,
  3        num_rows,
  4        data_mb,
  5        indx_mb,
  6        lob_mb,
  7        total_mb
  8         from (select data.table_name,
  9                 partitioning_type
 10                || decode (subpartitioning_type,
 11                           'none', null,
 12                           '/' || subpartitioning_type)
 13                       partitioned,
 14                num_rows,
 15                nvl(data_mb,0) data_mb,
 16                nvl(indx_mb,0) indx_mb,
 17                nvl(lob_mb,0) lob_mb,
 18                nvl(data_mb,0) + nvl(indx_mb,0) + nvl(lob_mb,0) total_mb
 19                from (  select table_name,
 20                      nvl(min(num_rows),0) num_rows,
 21                      round(sum(data_mb),2) data_mb
 22                         from (select table_name, num_rows, data_mb
 23                             from (select a.table_name,
 24                                   a.num_rows,
 25                                   b.bytes/1024/1024 as data_mb
 26                                     from user_tables a, user_segments b
 27                                    where a.table_name = b.segment_name))
 28                    group by table_name) data,
 29                    (  select a.table_name,
 30                           round(sum(b.bytes/1024/1024),2) as indx_mb
 31                        from user_indexes a, user_segments b
 32                          where a.index_name = b.segment_name
 33                       group by a.table_name) indx,
 34                    (  select a.table_name,
 35                          round(sum(b.bytes/1024/1024),2) as lob_mb
 36                       from user_lobs a, user_segments b
 37                      where a.segment_name = b.segment_name
 38                       group by a.table_name) lob,
 39                      user_part_tables part
 40                where     data.table_name = indx.table_name(+)
 41                      and data.table_name = lob.table_name(+)
 42                      and data.table_name = part.table_name(+))
 43     order by table_name;

TABLE_NAME              PARTITIONED NUM_ROWS DATA_MB INDX_MB LOB_MB TOTAL_MB
----------------------- ----------- -------- ------- ------- ------ --------
BLOB_BASIC              NO             15000     3,5       7   8841  8851,50
BLOB_SECUREFILE_MEDIUM  NO             15000     4,5     ,06   5247  5251,56
BLOB_SECUREFILE_HIGH    NO             15000    4,44     ,06   5183  5187,50
CLOB_BASIC              NO             15000    1,69      27   8841  8869,69
CLOB_SECUREFILE_MEDIUM  NO             15000    3,69     ,06    323   326,75
CLOB_SECUREFILE_HIGH    NO             15000    3,56     ,06    290   293,62

6 linhas selecionadas.
O resultado acima nos mostra a comparação entre os níveis de compressão dos dados das colunas BLOB e CLOB. Podemos perceber que os níveis de compressão MÉDIA e ALTA são bem próximos. Neste caso, acredito que a compressão MÉDIA é suficiente, além de gerar menos overhead para o sistema. Podemos perceber também que a compressão da coluna CLOB foi muito maior em comparação com a compressão da coluna BLOB.

Um outro teste que realizei foi o de setar a opção DEDUPLICATE em conjunto com a opção COMPRESSION. Segue abaixo o resultado da comparação.

TABLE_NAME              PARTITIONED NUM_ROWS DATA_MB INDX_MB LOB_MB TOTAL_MB
----------------------- ----------- -------- ------- ------- ------ --------
BLOB_BASIC              NO             15000     3,5       7   8841  8851,50
BLOB_SECUREFILE_MEDIUM  NO             15000     4,5     ,06   2880  2884,56
BLOB_SECUREFILE_HIGH    NO             15000    4,44     ,06   2808  2812,50
CLOB_BASIC              NO             15000    1,69      27   8841  8869,69
CLOB_SECUREFILE_MEDIUM  NO             15000    3,69     ,06    320   323,75
CLOB_SECUREFILE_HIGH    NO             15000    3,56     ,06    288   291,62

6 linhas selecionadas.
Podemos verificar que, diferentemente da coluna CLOB, na coluna BLOB o nível de compressão teve um aumento bem significativo com o uso das opções COMPRESS+DEDUPLICATE. Para resumir, abaixo está um gráfico demonstrando um comparativo dos níveis de compressão das colunas LOB.



Para finalizar, abaixo eu demonstro que é possível exportar uma tabela que possui uma coluna LOB criada como SECUREFILE no Oracle 11g para um schema de banco de dados no Oracle 10g através da cláusula "version" do utilitário de exportação Export Data Pump.
C:\>expdp system/manager schemas=scott dumpfile=tabelas_secure_version10g version=10.2

Export: Release 11.2.0.3.0 - Production on Sex Nov 2 13:44:52 2012

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

Conectado a: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Iniciando "SYSTEM"."SYS_EXPORT_SCHEMA_01":  system/********
schemas=scott dumpfile=tabelas_secure_version10g version=10.2
Estimativa em andamento com o método BLOCKS...
Processando o tipo de objeto SCHEMA_EXPORT/TABLE/TABLE_DATA
Estimativa total usando o método de BLOCKS: 0 KB
Processando o tipo de objeto SCHEMA_EXPORT/USER
Processando o tipo de objeto SCHEMA_EXPORT/SYSTEM_GRANT
Processando o tipo de objeto SCHEMA_EXPORT/ROLE_GRANT
Processando o tipo de objeto SCHEMA_EXPORT/DEFAULT_ROLE
Processando o tipo de objeto SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processando o tipo de objeto SCHEMA_EXPORT/TABLE/TABLE
. . exportou "SCOTT"."BLOB_SECUREFILE_MEDIUM"         0 KB       0 linhas
. . exportou "SCOTT"."CLOB_SECUREFILE_MEDIUM"         0 KB       0 linhas
Tabela-mestre "SYSTEM"."SYS_EXPORT_SCHEMA_01" carregada/descarregada com sucesso
******************************************************************************
Conjunto de arquivos de dump para SYSTEM.SYS_EXPORT_SCHEMA_01 é:
  E:\ORACLE\ADMIN\BD02\DPDUMP\TABELAS_SECURE_VERSION10G.DMP
O job "SYSTEM"."SYS_EXPORT_SCHEMA_01" foi concluído com sucesso em 13:46:01


C:\>impdp system/manager schemas=scott dumpfile=tabelas_secure_version10g

Import: Release 10.2.0.5.0 - Production on Sex 2 Nov 2012 13:51:03

Copyright (c) 2003, 2007, Oracle.  All rights reserved.

Conectado a: Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Tabela-mestre "SYSTEM"."SYS_IMPORT_SCHEMA_01" carregada/descarregada com sucesso
Iniciando "SYSTEM"."SYS_IMPORT_SCHEMA_01":  system/********
schemas=scott dumpfile=tabelas_secure_version10g
Processando o tipo de objeto SCHEMA_EXPORT/USER
Processando o tipo de objeto SCHEMA_EXPORT/SYSTEM_GRANT
Processando o tipo de objeto SCHEMA_EXPORT/ROLE_GRANT
Processando o tipo de objeto SCHEMA_EXPORT/DEFAULT_ROLE
Processando o tipo de objeto SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processando o tipo de objeto SCHEMA_EXPORT/TABLE/TABLE
Processando o tipo de objeto SCHEMA_EXPORT/TABLE/TABLE_DATA
. . importou "SCOTT"."BLOB_SECUREFILE_MEDIUM"         0 KB       0 linhas
. . importou "SCOTT"."CLOB_SECUREFILE_MEDIUM"         0 KB       0 linhas
O job "SYSTEM"."SYS_IMPORT_SCHEMA_01" foi concluído com sucesso em 13:51:09

segunda-feira, 1 de outubro de 2012

Criando um banco de dados standby com o RMAN e abordando o ACTIVE DATA GUARD e o SNAPSHOT STANDBY no Oracle 11g

Olá,

No artigo de Julho/2011 eu demonstrei na prática como clonar um banco de dados Oracle no mesmo servidor, através do comando DUPLICATE DATABASE do RMAN nas versões 10g e 11g. O foco principal do artigo não foi somente o de demonstrar os passos para realização da clonagem, mas também o de apresentar uma inovação na versão 11g com a cláusula ACTIVE DATABASE do comando duplicate do RMAN. Neste artigo irei demonstrar novamente uma clonagem utilizando o RMAN do Oracle 11g (11.2.0.3) de forma a criar um banco de dados standby físico (physical standby). A intenção é mostrar não apenas os passos para a criação de um banco de dados standby físico em outro servidor utilizando o RMAN com a cláusula "for standby", como também demonstrar alguns recursos do 11g como o ACTIVE DATA GUARD e o SNAPSHOT STANDBY. Para fazer uma breve introdução, o conceito por trás do Data Guard na qual um banco de dados standby está presente, nada mais é do que uma tecnologia de proteção contra desastres de um banco de dados Oracle. Nesta arquitetura temos um banco de dados primário (primary database) e um ou mais bancos de dados standby (standby database). Os bancos de dados estão conectados por uma rede que serve todas as transações à partir do banco de dados primário e depois as aplica ao banco de dados standby. Em essência, temos um banco de dados ativo e um banco de dados em recuperação constante. Portanto, o banco de dados standby físico é uma cópia bloco a bloco do banco de dados primário. Em relação aos modos de proteção dos dados, temos as seguintes opções:
  • No modo Maximum Performance (o padrão), as transações sofrem commit antes de suas informações de redo serem enviadas a um destino de banco de dados standby. Os commits no banco de dados primário ocorrem assim que as gravações nos arquivos de redo log online são concluídas.
  • No modo Maximum Availability, ao menos um destino de banco de dados standby deve ser gravado antes que uma transação sofra commit no banco de dados primário. Caso não seja possível, por causa algum tipo de falha, o banco de dados primário não sofrerá shutdown. Quando a falha é corrigida, os dados de redo que foram gerados à partir dela é transportado e aplicado no banco de dados standby.
  • No modo Maximum Protection, ao menos um destino de banco de dados standby deve ser gravado antes que uma transação sofra commit no banco de dados primário. Caso não seja possível, por causa de algum tipo de falha, o banco de dados primário sofrerá shutdown.
A arquitetura do Data Guard nos permite configurar os bancos dedos primário e standby com diferentes estruturas de diretórios para os arquivos de banco de dados, mas para facilitar o entendimento e a didática, irei assumir que os mesmos possuem estruturas de diretórios idênticos. No mais, segue abaixo a estrutura que utilizarei na criação do ambiente:


SERVER NAME ROLE     DB_NAME  DB_UNIQUE_NAME DATABASE FILES PATH          TNS SERVICE
----------- -------- -------- -------------- ---------------------------- -----------
linux1      PRIMARY  BD01     BD01           /u01/app/oracle/oradata/BD01 BD01_PRI
linux2      STANDBY  BD01     BD01_STBY      /u01/app/oracle/oradata/BD01 BD01_STBY

 
Bom, o primeiro passo é configurar o banco de dados primário. Vale a pena salientar que o banco de dados precisa estar obrigatoriamente operando no modo ARCHIVELOG e que a geração de redo forçada (force logging) esteja habilitada. Abaixo vocês verão que irei criar 4 grupos de arquivos de redo log standby no banco de dados primário. Estes arquivos são similares aos arquivos de redo log online, a única diferença é que os arquivos de redo log standby são usados para receber registros de redo provenientes do banco de dados primário quando for utilizado como modo de transporte dos registros de redo, o processo LGWR. Vale a pena salientar que isso é independente dos modos de proteção (Maximum Protection/Performance/Availability). Embora os arquivos de redo log standby sejam utilizados somente enquanto o banco de dados está executando em modo standby (standby role), a Oracle recomenda que criemos também arquivos de redo log standby no banco de dados primário de forma que uma eventual troca de roles (switchover, failover) entre os mesmos, aconteça de forma rápida e sem a necessidade de uma intervenção do DBA. Como regra e boa prática, é recomendável criarmos os arquivos de redo log standby do mesmo tamanho dos arquivos de redo log online (no meu caso 50 MB) e com um grupo a mais, ou seja, se meu banco de dados possui 3 grupos de redo log online (1, 2 e 3), então deverão ser criados 4 grupos de redo log standby (4, 5, 6 e 7). Vamos então para a prática.
========
PRIMARIO
========

[oracle@linux1 ~]$ export ORACLE_SID=BD01
[oracle@linux1 ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Seg Out 1 08:11:18 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Conectado a uma instância inativa.

SQL> startup mount
Instância ORACLE iniciada.

Total System Global Area  943718400 bytes
Fixed Size                  2077264 bytes
Variable Size             490737072 bytes
Database Buffers          444596224 bytes
Redo Buffers                6307840 bytes
Banco de dados montado.

SQL> alter database archivelog;

Banco de dados alterado.

SQL> alter database open;

Banco de dados alterado.

SQL> alter database force logging;

Banco de dados alterado.

SQL> alter database add standby logfile 
  2  group 4 ('/u01/app/oracle/oradata/BD01/redo04.log') size 50M;

Banco de dados alterado.

SQL> alter database add standby logfile 
  2  group 5 ('/u01/app/oracle/oradata/BD01/redo05.log') size 50M;

Banco de dados alterado.

SQL> alter database add standby logfile 
  2  group 6 ('/u01/app/oracle/oradata/BD01/redo06.log') size 50M;

Banco de dados alterado.

SQL> alter database add standby logfile 
  2  group 7 ('/u01/app/oracle/oradata/BD01/redo07.log') size 50M;

Banco de dados alterado.

SQL> alter system set log_archive_config='dg_config=(BD01,BD01_STBY)';

Sistema alterado.

SQL> alter system set log_archive_dest_2='service=BD01_STBY lgwr async
  2  valid_for=(online_logfiles,primary_role) db_unique_name=BD01_STBY';

Sistema alterado.

SQL> alter system set fal_client='BD01_PRI';

Sistema alterado.

SQL> alter system set fal_server='BD01_STBY';

Sistema alterado.

SQL> alter system set log_archive_dest_state_2='ENABLE';

Sistema alterado.

SQL> alter system set standby_file_management=AUTO;

Sistema alterado.

Após realizar as configurações acima, irei copiar o arquivo de inicialização spfile e o arquivo de senhas para o servidor linux2 conforme demonstrado abaixo:
[oracle@linux1 ~]$ cd $ORACLE_HOME/dbs
[oracle@linux1 ~]$ scp orapwBD01 spfileBD01.ora oracle@linux2:$ORACLE_HOME/dbs
oracle@linux2's password: ********
orapwBD01                         100% 1536     1.5KB/s   00:00
spfileBD01.ora                    100% 3584     3.5KB/s   00:00

Neste momento já irei configurar nos servidores linux1 e linux2 as entradas TNS para os serviços de banco de dados primário e standby. Os mesmos deverão ser configurados no arquivo tnsnames.ora localizado em $ORACLE_HOME/network/admin
BD01_PRI =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = linux1)(PORT = 1521))
    )
    (CONNECT_DATA =
     (SERVICE_NAME = BD01)
    )
  )

BD01_STBY =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = linux2)(PORT = 1521))
    )
    (CONNECT_DATA =
     (SERVICE_NAME = BD01)
    )
  )

Pronto. O banco de dados primário já está preparado para ser clonado. Com o software Oracle já instalado no servidor linux2, o próximo passo é criar as estruturas de diretórios necessários para o banco de dados standby e configurar os parâmetros necessários no arquivo de inicialização spfile. Como o banco de dados standby não será alvo de nenhuma política de backup, irei reduzir o tamanho da flash recovery area. Irei reduzir também os valores da SGA e PGA do banco de dados standby de forma a liberar recursos de memória para outros processos.
=======
STANDBY
=======

[oracle@linux2 ~]$ mkdir -p /u01/app/oracle/admin/BD01/adump
[oracle@linux2 ~]$ mkdir -p /u01/app/oracle/admin/BD01/dpdump
[oracle@linux2 ~]$ mkdir -p /u01/app/oracle/admin/BD01/pfile
[oracle@linux2 ~]$ mkdir -p /u01/app/oracle/oradata/BD01
[oracle@linux2 ~]$ export ORACLE_SID=BD01
[oracle@linux2 ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Seg Out 1 08:20:41 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Conectado a uma instância inativa.

SQL> startup nomount
Instância ORACLE iniciada.

Total System Global Area  313860096 bytes
Fixed Size                  1344652 bytes
Variable Size             104860532 bytes
Database Buffers          201326592 bytes
Redo Buffers                6328320 bytes

SQL> alter system set sga_target=300M scope=spfile;

Sistema alterado.

SQL> alter system set sga_max_size=300M scope=spfile;

Sistema alterado.

SQL> alter system set pga_aggregate_target=100M scope=spfile;

Sistema alterado.

SQL> alter system set db_recovery_file_dest_size=5G;

Sistema alterado.

SQL> alter system set db_unique_name='BD01_STBY'scope=spfile;

Sistema alterado.

SQL> alter system set log_archive_config='dg_config=(BD01,BD01_STBY)';

Sistema alterado.

SQL> alter system set log_archive_dest_2='service=BD01_PRI lgwr async 
  2  valid_for=(online_logfiles,primary_role) db_unique_name=BD01';

Sistema alterado.

SQL> alter system set fal_client='BD01_STBY';

Sistema alterado.

SQL> alter system set fal_server='BD01_PRI';

Sistema alterado.

SQL> alter system set log_archive_dest_state_2='ENABLE';

Sistema alterado.

SQL> startup nomount force;
Instância ORACLE iniciada.

Total System Global Area  313860096 bytes
Fixed Size                  1344652 bytes
Variable Size             104860532 bytes
Database Buffers          201326592 bytes
Redo Buffers                6328320 bytes
Pronto. Por enquanto, a instância standby ficará no estado NOMOUNT para ser utilizado como instância auxiliar pelo RMAN no processo de clonagem que será realizado mais à frente. Abaixo irei configurar o listener no servidor linux2 adicionando uma entrada para o banco de dados BD01 de forma que o RMAN reconheça a mesma como instância auxiliar no processo de clonagem através do comando DUPLICATE TARGET DATABASE ... FROM ACTIVE DATABASE.
[oracle@linux2 ~]$ cd $ORACLE_HOME/network/admin
[oracle@linux2 ~]$ cat listener.ora

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (GLOBAL_DBNAME = BD01)
      (ORACLE_HOME = /u01/app/oracle/product/11.2.0/dbhome_1)
      (SID_NAME = BD01)
    )
  )

LISTENER =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = linux2.localdomain)(PORT = 1521))
  )

ADR_BASE_LISTENER = /u01/app/oracle


[oracle@linux2 admin]$ lsnrctl reload

LSNRCTL for Linux: Version 11.2.0.3.0 - Production on 01-OUT-2012 08:27:38

Copyright (c) 1991, 2011, Oracle.  All rights reserved.

Estabelecendo conexão com (HOST=linux2.localdomain)(PORT=1521)
O comando foi executado com êxito

[oracle@linux2 admin]$ lsnrctl status

LSNRCTL for Linux: Version 11.2.0.3.0 - Production on 01-OUT-2012 08:27:54

Copyright (c) 1991, 2011, Oracle.  All rights reserved.

Estabelecendo conexão com (HOST=linux2.localdomain)(PORT=1521)

STATUS do LISTENER
------------------
Apelido             LISTENER
Versão              TNSLSNR for Linux: Version 11.2.0.3.0 - Production
Data Inicial        01-OUT-2012 08:27:28
Funcionamento       0 dias 0 hr. 0 min. 26 seg
Nível de Análise    off
Segurança           ON: Local OS Authentication
SNMP                OFF
Arq. Parâm. Listn.  /u01/app/oracle/product/11.2.0/dbhome_1/network/admin/listener.ora
Arq. Log Listener   /u01/app/oracle/diag/tnslsnr/linux2/listener/alert/log.xml
Resumo de Atendimento...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=linux2.localdomain)(PORT=1521)))
Resumo de Serviços...
O serviço "BD01" tem 1 instância(s).
  Instância "BD01", status UNKNOWN, tem 1 handler(s) para este serviço...
O serviço "BD01_STBY" tem 1 instância(s).
  Instância "BD01", status BLOCKED, tem 1 handler(s) para este serviço...
O comando foi executado com êxito

De volta ao servidor linux1, irei realizar a clonagem do banco de dados primário fazendo uso do RMAN com a opção "for standby" conforme demonstrado abaixo. Ao final, o mesmo será montado automaticamente.
========
PRIMARIO
========

[oracle@linux1 ~]$ rman target sys/manager@BD01_PRI auxiliary sys/manager@BD01_STBY

Gerenciador de Recuperação: Release 11.2.0.3.0 - Production on Seg Out 1 09:06:35 2012

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

conectado ao banco de dados de destino: BD01 (DBID=3060188332)
conectado ao banco de dados auxiliar: BD01 (não montado)

RMAN> duplicate target database for standby
   2  from active database
   3  dorecover nofilenamecheck;
Iniciando Duplicate Db em 01/10/12
usar o arquivo de controle do banco de dados de destino em vez do catálogo de recuperação
canal alocado: ORA_AUX_DISK_1
canal ORA_AUX_DISK_1: SID=19 tipo de dispositivo=DISK

conteúdo do Script de Memória:
{
   backup as copy reuse
   targetfile  '/u01/app/oracle/product/11.2.0/dbhome_1/dbs/orapwBD01' auxiliary format
 '/u01/app/oracle/product/11.2.0/dbhome_1/dbs/orapwBD01'   ;
}
executando Script de Memória

Iniciando backup em 01/10/12
canal alocado: ORA_DISK_1
canal ORA_DISK_1: SID=38 tipo de dispositivo=DISK
Finalizado backup em 01/10/12

conteúdo do Script de Memória:
{
   backup as copy current controlfile for standby auxiliary format  '/u01/app/oracle/oradata/BD01/control01.ctl';
   restore clone controlfile to  '/u01/app/oracle/oradata/BD01/control02.ctl' from
 '/u01/app/oracle/oradata/BD01/control01.ctl';
   restore clone controlfile to  '/u01/app/oracle/oradata/BD01/control03.ctl' from
 '/u01/app/oracle/oradata/BD01/control01.ctl';
}
executando Script de Memória

Iniciando backup em 01/10/12
utilizando o canal ORA_DISK_1
canal ORA_DISK_1: iniciando cópia de arquivo de dados
copiando arquivo de controle stand-by
nome do arquivo de saída=/u01/app/oracle/product/11.2.0/dbhome_1/dbs/snapcf_BD01.f tag=TAG20121001T150647 RECID=2 STAMP=793897607
canal ORA_DISK_1: cópia de arquivo de dados concluída; tempo decorrido: 00:00:03
Finalizado backup em 01/10/12

Iniciando restore em 01/10/12
utilizando o canal ORA_AUX_DISK_1

canal ORA_AUX_DISK_1: cópia do arquivo de controle copiada
Finalizado restore em 01/10/12

Iniciando restore em 01/10/12
utilizando o canal ORA_AUX_DISK_1

canal ORA_AUX_DISK_1: cópia do arquivo de controle copiada
Finalizado restore em 01/10/12

conteúdo do Script de Memória:
{
   sql clone 'alter database mount standby database';
}
executando Script de Memória

instrução sql: alter database mount standby database

conteúdo do Script de Memória:
{
   set newname for tempfile  1 to
 "/u01/app/oracle/oradata/BD01/temp01.dbf";
   switch clone tempfile all;
   set newname for datafile  1 to
 "/u01/app/oracle/oradata/BD01/system01.dbf";
   set newname for datafile  2 to
 "/u01/app/oracle/oradata/BD01/sysaux01.dbf";
   set newname for datafile  3 to
 "/u01/app/oracle/oradata/BD01/undotbs01.dbf";
   set newname for datafile  4 to
 "/u01/app/oracle/oradata/BD01/users01.dbf";
   backup as copy reuse
   datafile  1 auxiliary format
 "/u01/app/oracle/oradata/BD01/system01.dbf"   datafile
 2 auxiliary format
 "/u01/app/oracle/oradata/BD01/sysaux01.dbf"   datafile
 3 auxiliary format
 "/u01/app/oracle/oradata/BD01/undotbs01.dbf"   datafile
 4 auxiliary format
 "/u01/app/oracle/oradata/BD01/users01.dbf"   ;
   sql 'alter system archive log current';
}
executando Script de Memória

executando comando: SET NEWNAME

arquivo temporário renomeado 1 para /u01/app/oracle/oradata/BD01/temp01.dbf no arquivo de controle

executando comando: SET NEWNAME

executando comando: SET NEWNAME

executando comando: SET NEWNAME

executando comando: SET NEWNAME

Iniciando backup em 01/10/12
utilizando o canal ORA_DISK_1
canal ORA_DISK_1: iniciando cópia de arquivo de dados
número do arquivo=00001 nome=/u01/app/oracle/oradata/BD01/system01.dbf do arquivo de dados de entrada
nome do arquivo de saída=/u01/app/oracle/oradata/BD01/system01.dbf tag=TAG20121001T150658
canal ORA_DISK_1: cópia de arquivo de dados concluída; tempo decorrido: 00:00:56
canal ORA_DISK_1: iniciando cópia de arquivo de dados
número do arquivo=00002 nome=/u01/app/oracle/oradata/BD01/sysaux01.dbf do arquivo de dados de entrada
nome do arquivo de saída=/u01/app/oracle/oradata/BD01/sysaux01.dbf tag=TAG20121001T150658
canal ORA_DISK_1: cópia de arquivo de dados concluída; tempo decorrido: 00:00:45
canal ORA_DISK_1: iniciando cópia de arquivo de dados
número do arquivo=00003 nome=/u01/app/oracle/oradata/BD01/undotbs01.dbf do arquivo de dados de entrada
nome do arquivo de saída=/u01/app/oracle/oradata/BD01/undotbs01.dbf tag=TAG20121001T150658
canal ORA_DISK_1: cópia de arquivo de dados concluída; tempo decorrido: 00:00:26
canal ORA_DISK_1: iniciando cópia de arquivo de dados
número do arquivo=00004 nome=/u01/app/oracle/oradata/BD01/users01.dbf do arquivo de dados de entrada
nome do arquivo de saída=/u01/app/oracle/oradata/BD01/users01.dbf tag=TAG20121001T150658
canal ORA_DISK_1: cópia de arquivo de dados concluída; tempo decorrido: 00:00:01
Finalizado backup em 01/10/12

instrução sql: alter system archive log current

conteúdo do Script de Memória:
{
   backup as copy reuse
   archivelog like  "/u01/app/oracle/fast_recovery_area/BD01/archivelog/2012_10_01/o1_mf_1_20_8548b2bp_.arc" auxiliary format
 "/u01/app/oracle/fast_recovery_area/BD01_STBY/archivelog/2012_10_01/o1_mf_1_20_%u_.arc"   ;
   catalog clone recovery area;
   switch clone datafile all;
}
executando Script de Memória

Iniciando backup em 01/10/12
utilizando o canal ORA_DISK_1
canal ORA_DISK_1: iniciando cópia de log arquivado
thread do log arquivado de entrada=1 sequência=20 RECID=5 STAMP=793897746
nome do arquivo de saída=/u01/app/oracle/fast_recovery_area/BD01_STBY/archivelog/2012_10_01/o1_mf_1_20_0bnl3qoj_.arc RECID=0 STAMP=0
canal ORA_DISK_1: cópia de log arquivado concluída, tempo decorrido: 00:00:01
Finalizado backup em 01/10/12

procurando todos os arquivos na área de recuperação

Lista de Arquivos Desconhecidos para o Banco de Dados
=====================================================
Nome do Arquivo: /u01/app/oracle/fast_recovery_area/BD01_STBY/archivelog/2012_10_01/o1_mf_1_20_0bnl3qoj_.arc
catalogando arquivos...
catalogação concluída

Lista de Arquivos Catalogados
=============================
Nome do Arquivo: /u01/app/oracle/fast_recovery_area/BD01_STBY/archivelog/2012_10_01/o1_mf_1_20_0bnl3qoj_.arc

arquivo de dados 1 alternado para a cópia do arquivo de dados
cópia do arquivo de dados de entrada RECID=2 STAMP=793897748 file name=/u01/app/oracle/oradata/BD01/system01.dbf
arquivo de dados 2 alternado para a cópia do arquivo de dados
cópia do arquivo de dados de entrada RECID=3 STAMP=793897748 file name=/u01/app/oracle/oradata/BD01/sysaux01.dbf
arquivo de dados 3 alternado para a cópia do arquivo de dados
cópia do arquivo de dados de entrada RECID=4 STAMP=793897748 file name=/u01/app/oracle/oradata/BD01/undotbs01.dbf
arquivo de dados 4 alternado para a cópia do arquivo de dados
cópia do arquivo de dados de entrada RECID=5 STAMP=793897748 file name=/u01/app/oracle/oradata/BD01/users01.dbf

conteúdo do Script de Memória:
{
   set until scn  220833;
   recover
   standby
   clone database
    delete archivelog
   ;
}
executando Script de Memória

executando comando: SET until clause

Iniciando recover em 01/10/12
utilizando o canal ORA_AUX_DISK_1

iniciar recuperação de mídia

o log arquivado para thread 1 com sequência 20 já está no disco como arquivo /u01/app/oracle/fast_recovery_area/BD01_STBY/archivelog/2012_10_01/o1_mf_1_20_0bnl3qoj_.arc
nome do arquivo de log arquivado=/u01/app/oracle/fast_recovery_area/BD01_STBY/archivelog/2012_10_01/o1_mf_1_20_0bnl3qoj_.arc thread=1 sequência=20
recuperação da mídia concluída, tempo decorrido: 00:00:02
Finalizado recover em 01/10/12
Finalizado Duplicate Db em 01/10/12

Após finalizado o processo de clonagem acima, irei ativar o banco de dados standby em modo de recuperação (real-time apply) de forma que o mesmo comece a receber e aplicar os registros de redo vindos do banco de dados primário, conforme demonstrado abaixo. Irei configurar também uma política de retenção dos archived redo logs provindas do banco de dados primário de forma a protegê-los de qualquer deleção realizada pela política de backup RMAN configurada no lado do banco de dados primário.
=======
STANDBY
=======

[oracle@linux2 ~]$ export ORACLE_SID=BD01
[oracle@linux2 ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Seg Out 1 09:12:24 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning option

SQL> alter database recover managed standby database
  2  using current logfile disconnect from session;

Banco de dados alterado.

[oracle@linux2 ~]$ rman target /

Gerenciador de Recuperação: Release 11.2.0.3.0 - Production on Seg Out 1 09:13:09 2012

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

conectado ao banco de dados de destino: BD01 (DBID=3060188332, não aberto)

RMAN> configure archivelog deletion policy to applied on standby;

usar o arquivo de controle do banco de dados de destino em vez do catálogo
novos parâmetros de configuração RMAN:
configure archivelog deletion policy to applied on standby;
os novos parâmetros de configuração RMAN foram armazenados com sucesso

Com os bancos de dados primário e standby configurados e em pleno funcionamento, irei executar algumas queries em ambos os bancos de dados para verificar os seus status.
========
PRIMARIO
========

[oracle@linux1 ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Seg Out 1 09:14:47 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning option

SQL> select db_unique_name,protection_mode,protection_level,database_role
  2  from v$database;

DB_UNIQUE_NAME PROTECTION_MODE      PROTECTION_LEVEL     DATABASE_ROLE
-------------- -------------------- -------------------- ----------------
BD01           MAXIMUM PERFORMANCE  MAXIMUM PERFORMANCE  PRIMARY

1 linha selecionada.

SQL> select process,status,sequence#,block# from v$managed_standby;

PROCESS   STATUS        SEQUENCE#     BLOCK#
--------- ------------ ---------- ----------
ARCH      OPENING              19          0
ARCH      CLOSING              21          1
ARCH      OPENING              18          0
ARCH      CLOSING              21          1
LNS       WRITING              22        984

Podemos verificar pelos resultados acima que por padrão o banco de dados standby foi configurado para a proteção MAXIMUM PERFORMANCE e que o processo de servidor LNS (Log Network Server) está atualmente escrevendo dados de redo da sequência 22. A leitura que eu faço da arquitetura do Data Guard neste caso é a seguinte: Os registros de redo transmitidos pelo processo LNS no banco de dados primário são recebidos pelo banco de dados standby por um outro processo chamado RFS (Remote File Server), como será visto mais à frente. O processo LNS lê os mesmos registros de redo provenientes do buffer de redo log na SGA e os transmite ao banco de dados standby através do Oracle NET Services. O processo RFS recebe os dados de redo no banco de dados standby e aplica os mesmos nos arquivos de redo log standby (standby redo log files). Neste momento, o processo MRP (Managed Recovery Process) obtém os dados dos arquivos de redo log standby e os aplica nos datafiles do banco de dados standby. Como eu configurei o real-time apply, os dados de redo recebidos, são aplicados imediatamente nos arquivos de banco de dados, sem a necessidade de aguardar que os arquivos de redo log standby sejam arquivados. A figura abaixo demonstra um pouco desta arquitetura.


Para ilustrar ainda um pouco mais sobre esse mecanismo, a imagem abaixo retirada da documentação oficial do Data Guard no site da Oracle, nos mostra com mais clareza os componentes envolvidos. Saliento novamente que, por padrão, os dados de redo não são aplicados no banco de dados standby até que o arquivo de redo log standby seja arquivado. Quando o recurso real-time apply é usado, os dados de redo são aplicados no banco de dados standby quando forem recebidos, reduzindo potencialmente o tempo necessário para se fazer uma operação de failover.
 
 

Abaixo irei verificar no banco de dados standby os status de recebimento e aplicação dos dados de redo vindos do banco de dados primário.
=======
STANDBY
=======

[oracle@linux2 ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Seg Out 1 09:14:58 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning option

SQL> select db_unique_name,protection_mode,protection_level,database_role
  2  from v$database;

DB_UNIQUE_NAME PROTECTION_MODE      PROTECTION_LEVEL     DATABASE_ROLE
-------------- -------------------- -------------------- ----------------
BD01_STBY      MAXIMUM PERFORMANCE  MAXIMUM PERFORMANCE  PHYSICAL STANDBY

1 linha selecionada.

SQL> select process,status,sequence#,block# from v$managed_standby;

PROCESS   STATUS        SEQUENCE#     BLOCK#
--------- ------------ ---------- ----------
ARCH      CONNECTED             0          0
ARCH      CONNECTED             0          0
ARCH      CONNECTED             0          0
ARCH      CLOSING              21          1
RFS       IDLE                  0          0
RFS       IDLE                  0          0
RFS       IDLE                 22       1001
MRP0      APPLYING_LOG         22       1001

8 linhas selecionadas.

Acima podemos verificar que o processo RFS já terminou de escrever (neste momento) os dados de redo da sequência 22 nos arquivos de redo log standby e que o processo MRP já está aplicando os mesmos nos datafiles do banco de dados.


Sobre o ACTIVE DATA GUARD

 

Bom, com o banco de dados standby em pleno funcionamento, irei realizar um teste para demonstrar uma nova funcionalidade do Data Guard no Oracle 11g que se chama ACTIVE DATA GUARD. Nas versões anteriores ao Oracle 11g, um banco de dados standby físico pode ser aberto em modo somente leitura (read only). No entanto, a aplicação dos dados de redo provenientes do banco de dados primário será pausado, ou seja, os dados de redo continuarão sendo enviados para o banco de dados standby, mas o mesmos não serão aplicados nos arquivos de banco de dados até que o banco de dados standby seja colocado novamente no modo de recuperação. No Oracle 11g, podemos abrir o banco de dados standby em modo leitura e, mesmo assim, os dados de redo continuarão sendo aplicados no banco de dados standby, como veremos abaixo.
=======
STANDBY
=======

[oracle@linux2 ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Seg Out 1 09:38:30 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning option

SQL> alter database open read only;
alter database open read only
*
ERRO na linha 1:
ORA-10456: cannot open standby database; media recovery session may be in progress

Acima, eu quis demonstrar que não bastará apenas abrir o banco de dados em modo somente leitura. Será necessário cancelar o modo de recuperação, abrir o banco de dados em modo de somente leitura para, então, colocá-lo novamente em modo de recuperação.
SQL> alter database recover managed standby database cancel;

Banco de dados alterado.

SQL> alter database open read only;

Banco de dados alterado.

SQL> alter database recover managed standby database
  2  using current logfile disconnect from session;

Banco de dados alterado.

Com o banco de dados standby aberto como somente leitura, irei realizar algumas operações remotas no banco de dados primário e verificar se as alterações foram enviadas e aplicadas no banco de dados standby.
========
PRIMARIO
========

C:\>sqlplus system/manager@linux1/BD01

SQL*Plus: Release 11.2.0.3.0 - Production on Seg Out 1 09:41:27 2012

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning option

SYSTEM@linux1/BD01> create user scott identified by tiger default tablespace users;

Usuário criado.

SYSTEM@linux1/BD01> grant connect,resource to scott;

Concessão bem-sucedida.

SYSTEM@linux1/BD01> create table scott.t1 (id number);

Tabela criada.

SYSTEM@linux1/BD01> insert into scott.t1 select level from dual connect by level <=10;

10 linhas criadas.

SYSTEM@linux1/BD01> commit;

Commit concluído.
Acima eu criei o usuário SCOTT e populei a tabela T1 com dez registros. Irei executar uma consulta remota no banco de dados standby de forma a verificar se os mesmos já se encontram presentes no mesmo.
C:\>sqlplus system/manager@linux2/BD01

SQL*Plus: Release 11.2.0.3.0 - Production on Seg Out 1 09:51:21 2012

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning option

SYSTEM@linux2/BD01> select * from scott.t1;

        ID
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10

10 linhas selecionadas.

Perfeito. Podemos ver acima que os registros de redo foram enviados e aplicados no banco de dados standby mesmo ele estando aberto como somente leitura.


Sobre o SNAPSHOT STANDBY

 

Bom, uma outra feature (característica) que foi lançada na versão 11g é o SNAPSHOT STANDBY. Quando alteramos o banco de dados de physical standby para snapshot standby, o mesmo se comporta como se tivéssemos temporariamente fazendo um failover e abrindo o mesmo no modo READ WRITE, ou seja, o mesmo estaria operando como um banco de dados primário. Neste caso, a aplicação dos dados de redo são pausadas até que o mesmo volte a ser um physical standby. Para isso, o Oracle faz uso da tecnologia Flashback Database e internamente cria um ponto de restauração garantido como poderemos ver abaixo:
=======
STANDBY
=======

[oracle@linux2 ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Seg Out 1 10:11:27 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Conectado a uma instância inativa.

SQL> startup mount
Instância ORACLE iniciada.

Total System Global Area  313860096 bytes
Fixed Size                  1344652 bytes
Variable Size             104860532 bytes
Database Buffers          201326592 bytes
Redo Buffers                6328320 bytes
Banco de dados montado.

SQL> alter database convert to snapshot standby;

Banco de dados alterado.

SQL> alter database open;

Banco de dados alterado.

SQL> select flashback_on FROM v$database;

FLASHBACK_ON
------------------
RESTORE POINT ONLY

Neste momento, o banco de dados standby está aberto no modo de leitura/escrita. Abaixo podemos ver uma parte do arquivo de alerta que mostra a criação do ponto de restauração.
Mon Oct 1 10:12:44 2012
ALTER DATABASE CONVERT TO SNAPSHOT STANDBY
Starting background process RVWR
Mon Oct 1 10:12:45 2012
RVWR started with pid=22, OS id=6660
Allocated 3981204 bytes in shared pool for flashback generation buffer
Created guaranteed restore point SNAPSHOT_STANDBY_REQUIRED_10/01/2012 10:12:44
krsv_proc_kill: Killing 3 processes (all RFS)
Begin: Standby Redo Logfile archival
Mon Oct 1 10:16:38 2012
ARC0: STARTING ARCH PROCESSES
Mon Oct 1 10:16:38 2012
ARC1 started with pid=19, OS id=6678
Mon Oct 1 10:16:38 2012
ARC2 started with pid=20, OS id=6680
ARC1: Archival started
ARC2: Archival started
ARC2: Becoming the heartbeat ARCH
ARC2: Becoming the active heartbeat ARCH
Mon Oct 1 10:16:38 2012
ARC3 started with pid=21, OS id=6682
Archived Log entry 5 added for thread 1 sequence 24 ID 0xb666f6ac dest 1:
Mon Oct 1 10:16:38 2012
End: Standby Redo Logfile archival
RESETLOGS after incomplete recovery UNTIL CHANGE 225380
Waiting for all non-current ORLs to be archived...
All non-current ORLs have been archived.
Resetting resetlogs activation ID 3060201132 (0xb666f6ac)
Online log /u01/app/oracle/fast_recovery_area/BD01_STBY/onlinelog/o1_mf_1_8548b8jr_.log: Thread 1 Group 1 was previously cleared
Online log /u01/app/oracle/fast_recovery_area/BD01_STBY/onlinelog/o1_mf_2_8548b9xh_.log: Thread 1 Group 2 was previously cleared
Online log /u01/app/oracle/fast_recovery_area/BD01_STBY/onlinelog/o1_mf_3_8548bbro_.log: Thread 1 Group 3 was previously cleared
Standby became primary SCN: 225378
Mon Oct 1 10:16:38 2012
Setting recovery target incarnation to 2
CONVERT TO SNAPSHOT STANDBY: Complete - Database mounted as snapshot standby
Completed: ALTER DATABASE CONVERT TO SNAPSHOT STANDBY

Como teste, irei dropar o usuário SCOTT e então voltar o banco dados para physical standby de forma a verificar se o mesmo será novamente sincronizado com o banco de dados primário e se o usuário SCOTT será recuperado.
SQL> drop user scott cascade;

Usuário eliminado.

SQL> shutdown immediate;
Banco de dados fechado.
Banco de dados desmontado.
Instância ORACLE desativada.
SQL> startup mount;
Instância ORACLE iniciada.

Total System Global Area  313860096 bytes
Fixed Size                  1344652 bytes
Variable Size             104860532 bytes
Database Buffers          201326592 bytes
Redo Buffers                6328320 bytes
Banco de dados montado.

SQL> alter database convert to physical standby;

Banco de dados alterado.

SQL> shutdown immediate;
ORA-01507: banco de dados não montado

Instância ORACLE desativada.

SQL> startup nomount;
Instância ORACLE iniciada.

Total System Global Area  313860096 bytes
Fixed Size                  1344652 bytes
Variable Size             104860532 bytes
Database Buffers          201326592 bytes
Redo Buffers                6328320 bytes

SQL> alter database mount standby database;

Banco de dados alterado.

SQL> alter database recover managed standby database
  2  using current logfile disconnect from session;

Banco de dados alterado.

SQL> select flashback_on from v$database;

FLASHBACK_ON
------------
NO

Abaixo podemos ver no arquivo de log de alerta que o Oracle realizou uma operação de Flashback Database para retornar o banco de dados enquanto ele ainda era um physical standby.
Mon Oct 1 10:31:18 2012
alter database convert to physical standby
ALTER DATABASE CONVERT TO PHYSICAL STANDBY (BD01)
krsv_proc_kill: Killing 3 processes (all RFS)
Flashback Restore Start
Flashback Restore Complete
Drop guaranteed restore point
Stopping background process RVWR
Deleted Oracle managed file /u01/app/oracle/fast_recovery_area/BD01_STBY/flashback/o1_mf_854d1f21_.flb
Deleted Oracle managed file /u01/app/oracle/fast_recovery_area/BD01_STBY/flashback/o1_mf_854d1j9p_.flb
Guaranteed restore point  dropped
Clearing standby activation ID 3060197238 (0xb666e776)
The primary database controlfile was created using the
'MAXLOGFILES 16' clause.
There is space for up to 13 standby redo logfiles
Use the following SQL commands on the standby database to create
standby redo logfiles that match the primary database:
ALTER DATABASE ADD STANDBY LOGFILE 'srl1.f' SIZE 52428800;
ALTER DATABASE ADD STANDBY LOGFILE 'srl2.f' SIZE 52428800;
ALTER DATABASE ADD STANDBY LOGFILE 'srl3.f' SIZE 52428800;
ALTER DATABASE ADD STANDBY LOGFILE 'srl4.f' SIZE 52428800;
Shutting down archive processes
Archiving is disabled
Mon Oct 1 10:31:21 2012
ARCH shutting down
ARC3: Archival stopped
Mon Oct 1 10:31:21 2012
ARCH shutting down
ARC2: Archival stopped
Mon Oct 1 10:31:21 2012
ARCH shutting down
ARC1: Archival stopped
Mon Oct 1 10:31:21 2012
ARCH shutting down
ARC0: Archival stopped
Completed: alter database convert to physical standby

Após abrir novamente o banco de dados como somente leitura (como já demonstrado mais acima) poderemos verificar que o usuário SCOTT que foi dropado enquanto o banco de dados estava operando como SNAPSHOT DATABASE foi recuperado após a operação automática de Flashback Database.
C:\>sqlplus system/manager@linux2/BD01

SQL*Plus: Release 11.2.0.3.0 - Production on Seg Out 1 10:33:21 2012

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning option

SYSTEM@linux2/BD01> select * from scott.t1;

        ID
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10

10 linhas selecionadas.