terça-feira, 31 de maio de 2016

Duplicate de bases em RAC

Olá jovens... Espero que todos estejam bem...

Estou revisitando alguns posts, fazendo algumas melhorias e correções e acho que esse vale a pena.

Clonar bases em RAC é um processo bem tranquilo. Um pouco mais trabalhoso é verdade, mas bem tranquilo mesmo.

Nosso cenário:
Base: PROD
prod1 e prod1 ==> 11.2.0.4
CLONE
clone1 e clone2 ==> 11.2.0.4

Então, chega de papo. Vá até o seu local de backup e crie o arquivo "bkp.sh" com o seguinte conteúdo. Caso tenha o backup da noite (disco ou fita), melhor ainda.Utilize=o e ganhamos tempo.

export ORACLE_SID=prod1
rman target / <
run {
ALLOCATE CHANNEL d1 TYPE DISK MAXPIECESIZE 16G;
BACKUP AS COMPRESSED BACKUPSET FORMAT
            '/backup/prod/%d_%Y%M%D_%I_%s_%U.bkp' DATABASE
CURRENT CONTROLFILE FORMAT 
            '/backup/prod/ctrl_%d_%Y%M%D_%I_%s_%U.ctrl';
SQL 'ALTER SYSTEM CHECKPOINT';
SQL 'ALTER SYSTEM ARCHIVE LOG CURRENT';
BACKUP AS COMPRESSED BACKUPSET FORMAT 
            '/backup/prod/%d_%Y%M%D_%I_%s_%U.bkp' ARCHIVELOG ALL;
BACKUP CURRENT CONTROLFILE FORMAT 
            '/backup/prod/ctrl_%d_%Y%M%D_%I_%s_%U.ctrl';
RELEASE CHANNEL d1;
}
exit;
EOF

Faça um:
chmod +x bkp.sh
nohup ./bkp.sh &

E acompanhe pelo tail:
tail -f nohup.out

Enquanto rola o backup, podemos ir realizando outras tarefas. 

Se por ventura essa base que será clonada já existir no RAC, vamos limpar todos os arquivos antigos antes de continuar
srvctl stop database -d clone

Nunca é demais lembrar que se o clone for novo, precisamos criar toda a estrutura necessárias de diretórios, o password file, etc.

orapwd file=orapwclone1 entries=3 password=123456

mkdir -p $ORACLE_BASE/admin/CLONE/adump
mkdir -p $ORACLE_BASE/admin/CLONE/udump
mkdir -p $ORACLE_BASE/admin/CLONE/bdump
mkdir -p $ORACLE_BASE/admin/CLONE/cdump

Ou seja, toda a estrutura para uma base comum, nos dois nós do RAC.

Agora sim, vamos limpar os datafiles antigos via ASMCMD. Muito cuidado ao executar a limpeza.

su - grid
asmcmd -p
ls
cd +ASMGRP01
rm -rf clone
cd ..
cd +ASMGRP02
rm -rf clone

Após o término, não esqueça de voltar para o usuário ORACLE.

Vamos editar o init da base. Se a base for nova, vamos gerar um init a partir do spfile da produção. Além dos acertos normais, 
deve-se prestar muita atenção aos itens abaixo:
    
*.control_files                 = '+ASMGRP01','+ASMGRP02'
*.CLUSTER_DATABASE        = FALSE
*.DB_RECOVERY_FILE_DEST       = '+ASMGRP02'
*.DB_RECOVERY_FILE_DEST_SIZE   = 500G
*.DB_CREATE_FILE_DEST         = '+ASMGRP01'
*.db_create_online_log_dest_1 = '+ASMGRP01'
*.db_create_online_log_dest_2 = '+ASMGRP02'

## Configure o parâmetro para evitar um bug 
        ## durante o RESETLOGS do DUPLICATE:
_no_recovery_through_resetlogs=TRUE

Não se esqueça também que apesar da clonagem ser feita inicialmente como uma base single você está em um ambiente RAC. Por isso verifique também: 
    
    *.db_name='clone'
    clone1.instance_name='clone1'    
    clone2.instance_name='clone2'
    clone1.INSTANCE_NUMBER=1
    clone2.INSTANCE_NUMBER=2
    
   clone1.local_listener='(address=(protocol=tcp)(host=99.99.99.999)(port=1521))'
   clone2.local_listener='(address=(protocol=tcp)(host=99.99.99.999)(port=1521))'

    clone1.THREAD=1
    clone2.THREAD=2

    clone1.undo_tablespace='UNDOTBS01'
    clone2.undo_tablespace='UNDOTBS02'

É isso. Agora é só dar um startup nomount na criança.

export ORACLE_SID=clone1
SQL> startup nomount
    

Beleza meninão. Agora vamos preparar o script de clonagem. Eu gosto de usar o SET NEWNAME para setar os novos caminhos dos datafiles. Não tem erro. Outros preferem usar do convert, aqui fica a seu critério.

A brincadeira vai começar a ficar legal agora.. Até o momento, só preparamos a clonagem. Vamos agora a ação, propriamente dita...

Se conecte a produção e execute esse SELECT:

select 'set newname for datafile ' || FILE_ID || ' to new;' as tt 
   from dba_data_files order by FILE_ID;

select 'set newname for tempfile ' || FILE_ID || ' to new;' as tt 
   from dba_temp_files order by FILE_ID;

Você vai receber algo assim:
set newname for datafile 1 to new;
set newname for datafile 2 to new;

set newname for tempfile 1 to new;

Esse são os datafiles e temp que serão restaurados/criados durante o duplicate.

O "new" vai me trazer uma estrutura de pastas a partir do ASM dessa forma, por exemplo:
+ASMGRP01/clone/CLONE/datafile


Vamos agora montar o script. 

cd /backup/prod
vim clone.sh

export ORACLE_SID=CLONE
rman TARGET sys/123456@PRD_DUP1 NOCATALOG AUXILIARY / <
RUN 
{
ALLOCATE AUXILIARY CHANNEL aux1 DEVICE TYPE DISK 
           FORMAT '/backup/prod/%d_%Y%M%D_%I_%s_%U.bkp' ;
set until scn 19473867277;
set newname for datafile 1 to new;
        set newname for datafile 2 to new;
        set newname for datafile 3 to new;
        set newname for datafile 4 to new;
        set newname for datafile 5 to new;
        set newname for datafile 6 to new;
        set newname for datafile 7 to new;
        set newname for datafile 8 to new;
        set newname for datafile 9 to new;
        set newname for datafile 10 to new;
        set newname for datafile 11 to new;
        SET NEWNAME FOR TEMPFILE 1 TO new;
        DUPLICATE TARGET DATABASE TO "clone"
LOGFILE
GROUP 1 ('+ASMGRP01','+ASMGRP02') SIZE 300M REUSE,
GROUP 2 ('+ASMGRP01','+ASMGRP02') SIZE 300M REUSE,
GROUP 3 ('+ASMGRP01','+ASMGRP02') SIZE 300M REUSE;
}
EXIT
EOF

Alguns pontos importantes:
PRD_DUP1 => deve apontar para o nó 1 de produção e não para uma conexão do RAC

set until scn = número do ultimo SCN que consta em seu backup de archives. Digamos que o FULL terminou as 08:29:55 do dia 28. Nosso SCN seria "19473867277" pois ele é o último archive no backup após esse horário. Eu sempre pego o do nó 1. 

Identifique o seu último backup de archives, na coluna "LOW SCN":

BS Key  Size       Device Type Elapsed Time Completion Time
------- ---------- ----------- ------------ -------------------
15173   92.93M     DISK        00:00:18     30/05/2016 21:41:10
BP Key: 15173   Status: AVAILABLE  Compressed: YES  Tag: TAG20160530T214052
Piece Name: /backup/prod/backup_db_PROD_S_15201_P_1_T_913239652

 List of Archived Logs in backup set 15173
 Thrd Seq     Low SCN    Low Time            Next SCN   Next Time
 ---- ------- ---------- ------------------- ---------- ---------
 1    4739    19472084645 27/05/2016 21:41:53 19473855500 28/05/2016 06:01:01
 1    4740    19473855500 28/05/2016 06:01:01 19473867272 28/05/2016 08:30:07
 1    4741    19473867272 28/05/2016 08:30:07 19473867277 28/05/2016 08:30:11
 1    4742    19473867277 28/05/2016 08:30:11 19475563361 28/05/2016 13:31:56
 2    4774    19472083060 27/05/2016 21:41:52 19473867269 28/05/2016 08:30:06
 2    4775    19473867269 28/05/2016 08:30:06 19473867279 28/05/2016 08:30:12
 2    4776    19473867279 28/05/2016 08:30:12 19475563358 28/05/2016 13:31:53
 2    4777    19475563358 28/05/2016 13:31:53 19475563376 28/05/2016 13:32:04
 2    4778    19475563376 28/05/2016 13:32:04 19476477750 28/05/2016 21:41:56

REDO LOGFILE => Eu gosto de já setar um conjunto mínimo de redos na clonagem. Também fica a critério.


Agora vamos executar a clonagem.
chmod +x clone.sh
nohup ./clone.sh & 

E acompanhar:
tail -f nohup.out

Se você chegou até aqui, parabéns man... Sua base já está sendo clonada. 

Se deu algo errado, volta e faz de novo.. Não desite agora... 

Continuando... Se correu tudo bem, devemos ter sucesso na clonagem.

Agora vamos fazer umas validações antes de colocar a base no RAC:

- Verificar o nome do controlfile gerado:
select value from v$parameter where name = 'control_files';
VALUE
-------------------------------------------------------
'+ASMGRP1/clone/controlfile/current.309.772155755', 
'+ASMGRP2/clone/controlfile/current.276.772155755'

Vamos baixar a base clonada e acertar o init com o nome dos controlfiles e o cluster_database

shutdown immediate

vim $ORACLE_HOME/dba/inisclone1.ora
*.control_files = '+ASMGRP1/clone/controlfile/current.309.772155755', 
                  '+ASMGRP2/clone/controlfile/current.276.772155755'
*.CLUSTER_DATABASE = TRUE

startup


Bacana, subiu? Tudo certinho? Estamos quase lá... 

Agora vamos criar redos para a thread 2 se for necessário. Mas como eu vou saber? Acredite, a informação está lá :)
    
Vamos primeiro verificar o tamanho dos Redos para criar todos iguais

select * from v$log;         
select GROUP#, THREAD#, SEQUENCE#, BYTES* from v$log;        

    GROUP#    THREAD#  SEQUENCE#      BYTES
---------- ---------- ---------- ----------
         1          1          1  134217728 
         2          1          0  134217728
         3          1          0  134217728
         

Viu ali que só temos redos para a thread 1? E a thread 2, como fica? Vamos criar, ora pois :)
        

select  GROUP#, TYPE, MEMBER from v$logfile;

GROUP# TYPE    MEMBER
------ ------- ---------------------------------------
     1 ONLINE  +ASMGRP01/clone/CLONE/onlinelog/group_1.413.766579093
       +ASMGRP01/clone/CLONE/onlinelog/group_1.413.766579094
  
     2 ONLINE  +ASMGRP01/clone/CLONE/onlinelog/group_2.412.766579093
         +ASMGRP01/clone/CLONE/onlinelog/group_2.412.766579094
  
     3 ONLINE  +ASMGRP01/clone/CLONE/onlinelog/group_3.411.766579093
       +ASMGRP01/clone/CLONE/onlinelog/group_3.411.766579094
        
Executar uma vez para cada grupo (no nosso exem-):    
alter database add logfile thread 2 ('+ASMGRP01','+ASMGRP02') 
   size 300M;
alter system switch logfile;
    
Habilitar a thread 2, se necessário
       alter database enable thread 2;

Ai sim, criar um spfile, e copiar o init, orapw, spfile para o nó 2:


 scp initclone1.ora oracle@clone-02:/oracle/app/oracle/product/10.2.0/db_1/dbs/initclone2.ora
 scp orapwclone1 oracle@clone-02:/oracle/app/oracle/product/10.2.0/db_1/dbs/orapwclone2
 scp spfileclone1.ora oracle@clone-02:/oracle/app/oracle/product/10.2.0/db_1/dbs/spfileclone2.ora

E por fim, o momento mais legal de todos: Iniciar a base no cluster

Se for nova, registrar a base primeiro nos OCR's do RAC
        srvctl add database -d clone -o /oracle/app/oracle/product/10.2.0/db_1
srvctl add instance -d clone -i clone1 -n clone-01
srvctl add instance -d clone -i clone2 -n clone-02

Start da base pelo cluster 
       srvctl start database -d clone
    
AHAHAH, bacana.. Chegamos até aqui? Ai sim, então chegamos até o final.

Espero que tenha ajudado. E que seja um guia fácil de acompanhar.

Isso eu não posso mudar nunca, e fica como no original:

Ahh, já ia esquecendo de oferecer esse post para quatro caras que me ajudaram muito na minha transição para a consultoria: 

Fabio Telles
Everton Evaristo
Breno "Sabonetão" Rodrigues
Hélio Higa. 

Obrigado pela ajuda.

E lembrem-se que "friends will be friends right to the end - Queen".


Abraço
Mario

segunda-feira, 30 de maio de 2016

DataPump - Alguns comandos interessantes...

Boa noite jovens.

Você sabia que pode parar, continuar, ver o status e outras coisas quando está executando um export ou mesmo import via datapump?

Aqui vai uma dica bem legal quando estamos trabalhando com isso. 

Primeiro, vamos verificar os jobs atuais, em andamento. Para agilizar nosso exemplo, vamos considerar que temos um job já parado, com status "NOT RUNNING".

SET lines 140
COL owner_name FORMAT a10;
COL job_name FORMAT a20
COL state FORMAT a12 
COL operation LIKE owner_name
COL job_mode LIKE owner_name

SELECT 
owner_name, 
job_name, 
operation, 
job_mode, 
state, 
attached_sessions AS ATT_SESS 
FROM 
dba_datapump_jobs;









Agora começa a ficar legal. Vamos acessar o job que está parado. Para isso, vamos executar:

expdp \'/ as sysdba\' ATTACH=SYS_EXPORT_FULL_01

Será apresentado o seguinte resultado (ou algo próximo disso, dependendo da quantidade de worker's, do que você está executando, etc).

Job: SYS_EXPORT_FULL_01
 Owner: SYSTEM
 Operation: EXPORT
 Creator Privs: TRUE
 GUID: A5441357B472DFEEE040007F0100692A
 Start Time: Thursday, 08 June, 2011 20:23:39
 Mode: FULL
 Instance: db1
 Max Parallelism: 1
 EXPORT Job Parameters:
 Parameter Name      Parameter Value:
 CLIENT_COMMAND sys/******** full=y JOB_NAME=SYS_EXPORT_FULL_01
 State: IDLING
 Bytes Processed: 0
 Current Parallelism: 1
 Job Error Count: 0
 Dump File: /u01/app/oracle/admin/db1/dpdump/expdat.dmp
 bytes written: 520,192

Worker 1 Status:
 Process Name: DW00
 State: UNDEFINED

Agora, basta saber o que quer fazer. Veja os exemplos abaixo:

- Para apresentar o status
Export> status

-- Para matar o job
Export> KILL_JOB
Export> Are you sure you wish to stop this job ([yes]/no): yes

Importante: Ao matar o job, não será mais possível executar um RESUME ou START novamente. Também o registro para os dumpfiles serão removidos.
-- Para parar o job
Export> STOP_JOB=IMMEDIATE
Export> Are you sure you wish to stop this job ([yes]/no): yes
Importante: Adicionando '=IMMEDIATE' ao STOP_JOB, este não vai finalizar a tarefa atual. Ou seja, ao reiniciar o job essa tarefa deve ser refeita.
    -- Verificar o status após a parada
    SELECT 
owner_name, 
job_name, 
operation, 
job_mode, 
state
    FROM 
dba_datapump_jobs;










-- Para executar um Restart (expdp e impdp)

Export> START_JOB[=SKIP_CURRENT]
Export> CONTINUE_CLIENT

Job EXP_FULL has been reopened at Thursday, 09 June, 2011 10:26
Restarting "SYSTEM"."SYS_EXPORT_FULL_02":  sys/******** full=y JOB_NAME=SYS_EXPORT_FULL_01
Processing object type DATABASE_EXPORT/TABLESPACE
Processing object type DATABASE_EXPORT/PROFILE
Importante: Ao acrescentar o "SKIP_CURRENT", a tarefa atual não será realizada, passando ao próximo "registro" no dump.

-- Purge de job's "perdidos" com status "NOT RUNNING" na dba_datapump_jobs (expdp e impdp)

select 'DROP TABLE '||owner||'.'||table_name||';' from dba_tables where table_name like '%SYS%EXPORT%';

select 'DROP TABLE '||owner||'.'||table_name||';' from dba_tables where table_name like '%SYS%IMPORT%';

É isso jovem. Esses comandos já salvaram algumas migrações e restaurações que apresentaram algum problema e os job's simplesmente pararam sem erro algum. 

Um grande abraço
Mario

Postagem em destaque

[ORACLE] Batch change EDITIONABLE property.

Hello everyone. Hope you're doing well! Today, I have a simple case.   A test database had many database objects with the EDITIONABLE pr...