Eu sempre fui fã do backup feito na mão. Gosto de ter controle do processo, adaptar um script para demandas específicas etc e tal. Mas quando utilizamos o Oracle RAC, em geral estamos utilizando o ASM e neste caso, a unica forma de se fazer backup físico é pelo RMAN.
Clonar uma base guardada em file system é simples. Você copia os datafiles, gera um controlfile, edita ele, faz um PITR e pronto. Mas com ASM você não pode fazer isso. Você vai ter de utilizar o nosso amigo DUPLICATE.
Bom eu queria escrever algo detalhado, explicando cada passo, mas a preguiça me impediu. Então vamos apenas mostrar o cenário do exemplo e mandar bala logo:
- Oracle RAC 10.2.0.5 com 2 nós.
- A base a ser clonada é a ‘producao’ com as instâncias ‘producao1’ e ‘producao2’
- A base que vai ser atualizada/criada com os dados da’producao’ é a base’teste’ cin as instâncias ‘teste1’ e ‘teste2’.
- As duas bases tem suas instâncias nos dois servidores, ora1 e ora2 utilizando um SO UNIX like.
- A base ‘produção’ está utilizando o diskgroup ASMGRP01 e ASMGRP02.
- A base ‘teste’ utiliza apenas o diskgroup ASMGRP03
- Estou supondo que a base ‘teste’ já existe, e vai ser atualizada. Se a base não existir, o processo muda. Você não precisa apagar a base antes, mas precisa criar o init e os diretórios nos nós. Não vou abordar estes detalhes aqui.
Preparação
Passo1 – Verificar em quais diskgroups os arquivos da base teste estão
export ORACLE_SID=teste sqlplus "/ as sysdba" SELECT name FROM v$datafile; SELECT * FROM v$logfile; SELECT value FROM v$parameter WHERE name = 'control_files'; exit
Passo2 – baixar a base no RAC
srvctl stop database -d teste
Passo3 – Apagar a base no ASM
Tome cuidado aqui. Vamos excluir os arquivos dos diskgroups observados no passo 1.
export ORACLE_SID=+ASM asmcmd cd asmgrp03 rm -r teste exit
Passo 4 – Editar init
Aqui não estou utilizando SPFILE, pois o 10g tem um bug que ocorre com o SPFILE durante o duplicate no 10g)
cd $ORACLE_HOME/dbs vi initteste.ora
- Alterar os seguintes parâmetros :
*.control_files = '+ASMGRP03' *.cluster_database = FALSE
- Verifique o nome do parâmetro UNDO_TABLESPACE. O nome da tablespace deve ser idêntico ao UNDO da produção. No caso deste duplicate, tive de fazer um ajuste, pois o parâmetro do UNDO estava errado no init.
- Configure os parâmetros para alterar o diretório de destino. Neste caso, vamos mandar os datafiles e REDO dos diskgroups ASMGRP01 e ASMGRP02 todos para ASMGRP03:
db_file_name_convert = ('+ASMGRP01/producao','+ASMGRP03/teste', '+ASMGRP02/producao','+ASMGRP03/teste', '+ASMGRP03/producao','+ASMGRP03/teste') log_file_name_convert = ('+ASMGRP01/producao','+ASMGRP03/teste', '+ASMGRP02/producao','+ASMGRP03/teste', '+ASMGRP03/producao','+ASMGRP03/teste')
- Configure o parâmetro para evitar um bug durante o RESETLOGS do DUPLICATE:
_no_recovery_through_resetlogs=TRUE
Passo 5 – Fazer o backup em disco da base de produção
Se já houver um backup recente, pode pular esta etapa. É fundamental que no backup tenha ocorrido um
sql ‘alter system archive log current’;
No caso abaixo o
backup database plus archivelog
faz isso implicitamente. Se resolver fazer o backup de outra forma, sembre-se disso.
export ORACLE_SID=producao rman target / <<EOF backup database plus archivelog; exit EOF
Enfim o DUPLICATE
Passo 6 – Realizar o DUPLICATE com o nohup
É importante rodar isto com o nohup para evitar surpresas. Na verdade no próprio backup também é importante. Então eu crio o script:
export ORACLE_SID=teste rman TARGET sys/sua_senha@producao NOCATALOG AUXILIARY sys/sua_senha <<EOF RUN { ALLOCATE AUXILIARY CHANNEL aux1 DEVICE TYPE DISK; DUPLICATE TARGET DATABASE TO teste; RELEASE AUXILIARY CHANNEL aux1; } EXIT EOF
e rodo ele com o comando:
nohup atualizaTeste.sh
Verifique o log do nohup.out e veja se está ok.
tail -f nohup.out
Passo 7 – Verificar o controlfile gerado:
export ORACLE_SID=teste sqlplus "/ as sysdba" <<EOF SELECT value FROM v$parameter WHERE name = 'control_files'; exit EOF
Passo 8 – Baixar a base
export ORACLE_SID=teste sqlplus "/ as sysdba" <<EOF shutdown immediate exit EOF
Passo 9 – Editar novamente o init
Colocar o parâmetro ‘control_files’ com o valor encontrado no passo 7.
Colocar o parâmetro ‘cluster_database’ com o valor TRUE.
Copiar o init editado para o nó 2:
scp initteste01.ora ora2:$ORACLE_HOME/dbs/initteste2.ora
Passo 10 – Subir a base no modo NOARCHIVE
export ORACLE_SID=teste sqlplus "/ as sysdba" <<EOF startup mount ALTER database noarchivelog; ALTER database open; EOF
Habilitando a base no 2º nó
Se você chegou aqui, parabéns. Eu demorei um tempinho até fazer isso com sucesso pleno. Mas tem um detalhe importante, ao duplicar a base, apenas um nó é duplicado, o outro nó continua inativo.
Passo 11 – Verificar o UNDO
Verifique se o UNDO dos 2 nós estão presentes.
export ORACLE_SID=teste sqlplus "/ as sysdba" <<EOF SELECT tablespace_name, status FROM dba_tablespaces WHERE contents = 'UNDO'; exit EOF
Se não estiver, criar para o nó 2
Passo 12 – Criar o REDO para o nó 2
Antes verifique como os logs de REDO estão:
SELECT * FROM v$log; -- olhar o tamanho dos logs select * FROM v$logfile; -- olhar o destino e quantidade dos logs;
Depois crie os logs para a thread 2 segundo as informações encontradas. Neste caso, serão 4 REDOs, de 512M com um membro por grupo utilizando o diskgroup ASMGRP03.
ALTER database add logfile thread 2 ('+asmgrp03') size 512M; ALTER database add logfile thread 2 ('+asmgrp03') size 512M; ALTER database add logfile thread 2 ('+asmgrp03') size 512M; ALTER database add logfile thread 2 ('+asmgrp03') size 512M;
Passo 13 – Habilitar o nó 2
ALTER database enable thread 2;
Passo 14 – baixar a base
shutdown immediate
Passo 15 Verificar se a base está cadastrada no cluster
Como a base em geral está sendo atualizada, este problema não ocorre, pois já deverá estar OK. Mas se você estiver criando uma nova, com certeza vai precisar mexer nisso. De qualquer forma, é sempre bom checar.
cd $CRS_HOME/bin/crs_stat
Tem de aparecer algo como:
NAME=ora.teste.db TYPE=application TARGET=OFFLINE STATE=OFFLINE on ora02 NAME=ora.teste.teste1.inst TYPE=application TARGET=OFFLINE STATE=OFFLINE on ora01 NAME=ora.teste.teste2.inst TYPE=application TARGET=OFFLINE STATE=OFFLINE on ora02
Se não aparecerem as linhas, cadastrar a base no cluster:
srvctl add database -d teste -o $ORACLE_HOME srvctl add instance -d teste -i teste1 -n ora01 srvctl add instance -d teste -i teste2 -n ora02
Legenda:
- -d = nome da base
- -o = local do $ORACLE_HOME
- -I = instância
- -n = mome do servidor onde a instância está.
Passo 16 – Subir a base nos 2 nós pelo cluster
srvctl start database -d teste
Passo 17 – Verificar nos 2 nós
Você pode ignorar alguns erros logo depois do recover, no momento do resetlogs, mas fique atendo a todo o restante.
Na verdade, durante todo o processo é uma boa idéia ir acompanhando o alerta.
E se a base de testes não está no mesmo servidor, não está em RAC e ASM?
Bom, aí meu caro, você vai ter que tomar alguns cuidados adicionais:
- Você vai rodar o duplicate no servidor onde está a base de testes. Então precisa configurar o tnsnames.ora para a produção neste servidor. Configure para se conectar em um nó apenas.
- Você vai ter que tomar mais cuidado ainda no FILE_NAME_CONVERT.
- Você vai ter de criar os logs de REDO no próprio comando DUPLICATE.
- Você provavelmente vai pegar o backup da fita, então pegue a string de conexão do backup, mas altere o canal (channel em inglês) para auxiliar.
- Você vai poder apagar o tablespace UNDO do nó 2 que você não vai precisar.
- Você vai ter de tomar um mega cuidado com os archives. Quando o DUPLICATE roda com a produção e testes no mesmo servidor, nós configuramos o archive_dest da base teste igual ao da base de produção e não nos preocupamos mais com isso. Agora estão em servidores diferentes…
Exemplo 1
Ao invés de ficar aqui explicando tudo novamente, vou mostrar 2 exemplos de DUPLICATE de uma base em RAC para uma base de testes sem RAC:
rman TARGET sys/sua_senha@producao NOCATALOG AUXILIARY / <<EOF run { allocate auxiliary channel 'dev_0' type 'sbt_tape' parms 'SBT_LIBRARY=/opt/omni/lib/libob2oracle8_64bit.so, ENV=(OB2BARTYPE=Oracle8, OB2APPNAME=producao01, OB2BARLIST=PRODUCAO_DIARIO)'; send device type 'sbt_tape' 'OB2BARHOSTNAME=ora1'; DUPLICATE TARGET DATABASE TO teste UNTIL TIME "TRUNC(SYSDATE) + 3/24 + 40/1440" DB_FILE_NAME_CONVERT = ( '+ASMGRP01/producao/datafile/', '/u01/oradata/teste/', '+ASMGRP01/producao/tempfile/', '/u01/oradata/teste/') LOGFILE GROUP 1 ('/u01/oradata/teste/redo01.log') SIZE 128M REUSE, GROUP 2 ('/u01/oradata/teste/redo02.log') SIZE 128M REUSE, GROUP 3 ('/u01/oradata/teste/redo03.log') SIZE 128M REUSE, GROUP 4 ('/u01/oradata/teste/redo04.log') SIZE 128M REUSE; release auxiliary channel 'dev_0'; } EXIT EOF
Algumas observações:
- Este backup está vindo de uma fita, então estou passando alguns parâmetros específicos para um software de backup, que não vem ao caso aqui. Mas fique atento que sem os dados de um log de backup para fita da sua ferramenta de backup, você não tem como pegar os parâmetros em “params” logo no começo;
- O parâmetro “UNTIL TIME” está ajustado para um horário bem específico, referente à janela de backup da unidade de fitas, e é um horário depois do término do backup normal e antes do horário em que eu rodei um backup dos archives na mão;
- Eu coloquei o DB_FILE_NAME_CONVERT aqui no DUPLICATE e não no INIT. Se estiver configurado este parâmetro no init também, vale o do DUPLICATE.
- O LOGFILE é um dos segredos do sucesso. Ao invés de configurar o LOG_FILE_NAME_CONVERT, eu digo logo como ele tem de ficar. Me poupa o trabalho de arrumar isso depois.
- Você não precisa, mas seria bom remover o tablespace de UNDO do nó 2, que não estará sendo utilizado.
Exemplo 2
Agora vou mostrar um script que utilizo para uma base D-1. Ou seja, uma base que faz o restore do backup da produção todo dia:
export ORACLE_HOME=/opt/app/oracle/product/10.2.0/db_1 export PATH=/usr/local/bin:/bin:/usr/bin:$ORACLE_HOME/bin export ORACLE_SID=teste sqlplus "/ as sysdba" <<EOF shutdown abort startup nomount EOF rm -fr /u01/oradata/teste/ rman TARGET sys/sua_senha@producao NOCATALOG AUXILIARY sys/sua_senha@teste <<EOF CONFIGURE DEVICE TYPE SBT_TAPE PARALLELISM 1 BACKUP TYPE TO BACKUPSET; CONFIGURE CHANNEL DEVICE TYPE 'SBT_TAPE' PARMS 'ENV=(OB2BARTYPE=Oracle8, OB2APPNAME=producao1, OB2BARLIST=PRODUCAO FULL)'; CONFIGURE DEFAULT DEVICE TYPE TO 'SBT_TAPE'; RUN { DUPLICATE TARGET DATABASE TO teste UNTIL TIME "TRUNC(SYSDATE) + 2/24 + 50/1440" OPEN RESTRICTED DB_FILE_NAME_CONVERT = ( '+ASMGRP01/producao/datafile/', '/u01/oradata/teste/', '+ASMGRP02/producao/datafile/', '/u01/oradata/teste/', '+ASMGRP01/producao/tempfile/', '/u01/oradata/teste/') LOGFILE GROUP 1 ('/u01/oradata/teste/redo01.log') SIZE 128M REUSE, GROUP 2 ('/u01/oradata/teste/redo02.log') SIZE 128M REUSE, GROUP 3 ('/u01/oradata/teste/redo03.log') SIZE 128M REUSE, GROUP 4 ('/u01/oradata/teste/redo04.log') SIZE 128M REUSE; } EXIT EOF
Comentários:
- Este script é uma forma simplificada de um script que fica agendado no crontab e roda toda noite.
- Note que a base abre no modo restrito.
Exemplo 3
Agora um exemplo diferente, um clone de uma base sem ASM e sem RAC. O backup via RMAN é feito toda a noite para disco e utilizamos este backup para o clone. Só que a base de produção e testes estão em servidores diferentes.
Para isso criei um arquivo no servidor de produção para ajudar em ~/script/copia_backup.sh com:
find /u01/backup/producao/ -name 'backup_db_*' -daystart -mtime -1 -exec rsync -av {} 192.168.0.x:/u01/backup/producao ;
Este arquivo serve só para copiar o backup da produção para o servidor de teste.
Agora vamos ao script para a atualização da base:
export ORACLE_SID=teste export ORACLE_SID_ORIGEM=producao export ORACLE_HOME=/opt/app/oracle/product/10.2.0/db_1 export PATH=$PATH:$ORACLE_HOME/bin sqlplus "/ as sysdba" << EOF shutdown abort exit 0 EOF if [ $? -ne 0 ]; then echo " ERRO: Banco de dados invalido !" exit; fi echo "Limpando a área de backup" find /u01/backup/$ORACLE_SID_ORIGEM/ 'backup_db_*' -mtime +2 -exec rm -fv {} ; echo "Limpando a área de archive" find /u01/archive/$ORACLE_SID_ORIGEM/ '*.arc' -mtime +1 -exec rm -fv {} ; echo "Backup dos archives" rman target sys/sua_senha@$ORACLE_SID_ORIGEM <<EOF backup archivelog all; exit EOF echo "Copiando o backup da produção" ssh 192.168.0.y ~/script/copia_backup.sh echo "Limpando a base $ORACLE_SID" rm -Rfv /u01/oradata/${ORACLE_SID}/*.dbf echo "Subindo a base $ORACLE_SID no modo NOMOUNT" sqlplus "/ as sysdba" << EOF startup nomount; exit EOF echo "Iniciando o DUPLICATE" rman target 'sys/sua_senha@$ORACLE_SID_ORIGEM' auxiliary / <<EOF CONFIGURE CHANNEL DEVICE TYPE DISK FORMAT '/u01/backup/$ORACLE_SID_ORIGEM/backup_db_%U_%T' MAXPIECESIZE 8192 M; run { allocate auxiliary channel dsk11 device type disk; duplicate target database to $INSTANCE_NAME until time "sysdate - 12/24"; release auxiliary channel dsk11; } exit EOF sqlplus "/ as sysdba" << EOF shutdown immediate; startup mount; ALTER DATABASE NOARCHIVELOG; ALTER DATABASE OPEN; exit EOF
Comentários:
- A troca de chaves SSH foi realizada para podermos rodar comandos SSH sem senha;
- O backup via RMAN tem de ser copiado do servidor de produção para o servidor de teste para exatamente o mesmo diretório.
- Sempre cuidado com os archives. Fazemos um backup via RMAN dos archives e copiamos ele junto antes de iniciar o DUPLICATE;
- Colocamos a base no modo noarchive, algumas pessoas fazem isso em bases de testes.
Referências
- Backup and Recovery Advanced User’s Guide – Cap 13: Creating and Updating Duplicate Databases with RMAN
- Backup and Recovery Reference – DUPLICATE