sábado, 29 de outubro de 2016

Erro ao executar um duplicate database em grid 12c (12.1.0.2.0)

Fala pessoal.

Hoje, montando um ambiente de testes aqui para um artigo de GoldenGate me deparei com um erro chatinho quando fui executar um duplicate from active database.

Estava com todas as configurações corretas, usando o script abaixo:

RUN 
{
set until scn 2462883;
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 tempfile 1 to new;
DUPLICATE TARGET DATABASE TO "ggcopy" FROM ACTIVE DATABASE;
}

Nada demais, clonando uma base de testes.

Estava tomando o erro:

sql statement: alter system set  db_unique_name =  ''GGCOPY'' comment= ''Modified by RMAN duplicate'' scope=spfile
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 10/29/2016 09:12:29
RMAN-05501: aborting duplication of target database
RMAN-03015: error occurred in stored script Memory Script
RMAN-03009: failure of sql command on clone_default channel at 10/29/2016 09:12:29
RMAN-11003: failure during parse/execution of SQL statement: alter system set  db_unique_name =  'GGCOPY' comment= 'Modified by RMAN duplicate' scope=spfile
ORA-32017: failure in updating SPFILE
ORA-65500: could not modify DB_UNIQUE_NAME, resource exists


Pesquisando um pouco no MOS, como diz minha linda esposa, PAH!!! estava lá, um bug.

Note: ORA-65500 When Trying To Duplicate a Database (Doc ID 1963620.1)

Neste note, é referenciao um bug: Bug 20977794 - RMAN-3002 ORA-65500 from RMAN duplicate on RAC (Doc ID 20977794.8)

Existe um patch disponível, mas como não é o meu foco acertar essa base, eu nem apliquei.



Então, confirmando o bug, a minha base de testes estava registrada no meu grid, e por isso era dada a mensagem que o resource já existia.


Para resolver foi simples, aplicando o workaround do note, ou seja, excluir a base a ser clonada do grid e adicionar após o processo.

[ggcopy.rac12c1 dbs]$ srvctl remove database -d ggcopy
Remove the database ggcopy? (y/[n]) y

É isso, abraço.
Mario

terça-feira, 18 de outubro de 2016

Backup e restore de estatísticas

Hola que tal?

Eita, ahahahaha não ficou bom isso...

Opa, pessoal.. Como estão? 

Agora sim.

Em primeiro lugar, fique claro que esse texto é somente uma pequena introdução sobre o assunto. Alias nem entramos no assunto das estatísticas.

Aqui estamos abordando somente uma ou duas formas de fazer backup's de estatísticas.

Se você quer conhecer melhor sobre a coleta de estatísticas, aqui vão alguns links excelentes:


Só alguns exemplos dos excelentes textos e documentos obrigatórios da Oracle que nós achamos sobre o assunto. 

Este note do MOS também é ótimo: "FAQ: Automatic Statistics Collection (Doc ID 1233203.1)"

Dito isso, então deixemos de mimimi e vamos ao que interessa.

Esses dias me aconteceu um pequeno problema com estatísticas de um banco. Depois da coleta, a performance de várias tabelas ficou pior do que anteriormente. 

Com isso e com aquela falta de paciência normal dos usuários, veio a pergunta premiada. Tem como voltar essas tais estatísticas?

Claro, pequeno gafanhoto. Nós somos DBA's e DBA que se preze não faz nada sem backup (e testa o restore disso também, é claro).

Então, é isso que eu quero mostrar. Como fazer o "backup das estatísticas" de um schema e depois retornar isso em caso de necessidade. 

Coisa simples mas que muitos esquecem antes de fazer uma intervenção dessas.

Coloquei o "backup" entre aspas pois na verdade faremos um EXPORT/IMPORT das estatísticas no nosso primeiro exemplo. Já no caso do backup feito pelo próprio Oracle, que é o nosso segundo exemplo, trataremos de um restore propriamente dito.

Vamos ver primeiro as tabelas e suas estatísticas de um usuário qualquer. Vou usar o SQL abaixo (e deixar a parte da tabela comentada pois vamos usar mais tarde).

COL OWNER FOR A10;
COL TABLE_NAME FOR A15;

SELECT  
owner, 
table_name, 
avg_row_len, 
blocks, 
empty_blocks, 
num_rows, 
TO_CHAR(last_analyzed, 'DD/MM/YYYY HH24:MI:SS') as last_analyzed
FROM    
DBA_TABLES
WHERE   
OWNER = UPPER(NVL('&OWNER',OWNER));

OWNER      TABLE_NAME      AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS   NUM_ROWS LAST_ANALYZED
---------- --------------- ----------- ---------- ------------ ---------- -------------------
EXEMPLO    EXTCONFIG                44          1            0         11 10/09/2016 23:02:50
EXEMPLO    SERVER_STAT               0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TMP_TOP_SESSION           0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TOP_SESSION               0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TOP_SQL_TEXT              0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TOP_SQL                   0          0            0          0 10/09/2016 23:02:50
EXEMPLO    LOG_TEMPO                33          5            0          5 13/10/2016 23:00:13
EXEMPLO    ESTATISTICA              75        244            0      19539 12/10/2016 23:00:04
EXEMPLO    DISPONIBILIDADE          19         20            0         26 13/10/2016 23:00:11
EXEMPLO    BACKUP                    0          0            0          0 10/09/2016 23:02:50
EXEMPLO    MENSAGEM                 86        496            0      37974 10/10/2016 23:00:06
EXEMPLO    INSTANCIA                 0         28            0          0 13/10/2016 23:00:13
EXEMPLO    ESQUEMA                  18         13            0       1641 10/10/2016 23:00:05
EXEMPLO    TABLESPACE               24         13            0       2550 28/09/2016 23:00:03
EXEMPLO    HIST_ARMAZENAME           0          0            0          0 10/09/2016 23:02:50
EXEMPLO    ARMAZENAMENTO             0          0            0          0 10/09/2016 23:02:50
EXEMPLO    SERVIDOR                  0         28            0          0 13/10/2016 23:00:13
EXEMPLO    CONFIG                   27          5            0          1 10/09/2016 23:02:50

Ok, bem zuadas algumas estatísticas por sinal, bem antigas.

Vamos ver como forçar um "backup" das estatísticas antes da coleta. Depois coletamos as estatísticas e vamos retornar algumas.

Primeiro, faremos da forma manual. Vou rodar tudo como SYS:

1) Criar a tabela que irá armazenar a estatística
exec dbms_stats.create_stat_table(
      ownname  => 'EXEMPLO',
      stattab  => 'STAT_EXEMPLO',
      tblspace => 'EXEMPLOTBS'
);

Onde:
ownname - Name of the schema. 
stattab      - Name of the table to create. 
tblspace   - Tablespace in which to create the stat tables. If none is specified, then they are created in the user's default tablespace. 

2) Vamos agora exportar as estatísticas do schema
exec dbms_stats.export_schema_stats(
      ownname => 'EXEMPLO',
      stattab => 'STAT_EXEMPLO',
      statown => 'EXEMPLO'
);

Onde:
ownname  - Name of the schema. 
stattab      - User stat table identifier describing where to store the statistics. 
statown    - Schema containing stattab (if different than ownname). 

-- Se quiser, pode verificar se todas as tabelas estão no backup das estatísticas. 
select distinct c1 as tabelas from EXEMPLO.STAT_EXEMPLO where type ='T';

-- Como curiosidade, poderíamos salvar apenas a estatística de uma tabela:
exec dbms_stats.export_table_stats(
      ownname => 'EXEMPLO',
      tabname => 'MENSAGEM',
      statown => 'EXEMPLO',
      stattab => 'STAT_EXEMPLO',
      cascade => true);

3) Vamos coletar as estatísticas de uma tabela qualquer
EXEC DBMS_STATS.GATHER_TABLE_STATS(
OWNNAME=>'EXEMPLO',
TABNAME=>'MENSAGEM',
ESTIMATE_PERCENT=>DBMS_STATS.AUTO_SAMPLE_SIZE,
BLOCK_SAMPLE=>FALSE,
DEGREE=>16,
GRANULARITY=>'AUTO',
CASCADE=>TRUE,
NO_INVALIDATE=>FALSE,
METHOD_OPT=>'FOR ALL COLUMNS SIZE AUTO'
);

4) Conferimos as estatísticas
OWNER      TABLE_NAME      AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS   NUM_ROWS LAST_ANALYZED
---------- --------------- ----------- ---------- ------------ ---------- -------------------
EXEMPLO     MENSAGEM                 86        622            0      40312 15/10/2016 00:31:00

Lembrando que a estatítica antigas estava com a data do dia 10/10.

5) Vamos restaurar a posição anterior dessa estatística então
exec dbms_stats.import_table_stats(
ownname => 'EXEMPLO',
stattab => 'STAT_EXEMPLO',
tabname => 'MENSAGEM'
);

E olha que beleza, tudo restaurado como antes.

OWNER      TABLE_NAME      AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS   NUM_ROWS LAST_ANALYZED
---------- --------------- ----------- ---------- ------------ ---------- -------------------
EXEMPLO     MENSAGEM                 86        496            0      37974 10/10/2016 23:00:06

Ok, mas e se eu tivesse coletado do schema todo? Sem problemas.

1) Vamos coletar do schema todo. Lembrando que já criamos o backup de nosso schema.
exec dbms_stats.gather_schema_stats(
OWNNAME=>'EXEMPLO',
ESTIMATE_PERCENT=>DBMS_STATS.AUTO_SAMPLE_SIZE,
BLOCK_SAMPLE=>FALSE,
DEGREE=>16,
GRANULARITY=>'AUTO',
CASCADE=>TRUE,
NO_INVALIDATE=>FALSE,
OPTIONS=>'GATHER',
METHOD_OPT=>'FOR ALL COLUMNS SIZE AUTO'
);

2) Conferimos as estatísticas atuais.
OWNER      TABLE_NAME      AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS   NUM_ROWS LAST_ANALYZED
---------- --------------- ----------- ---------- ------------ ---------- -------------------
EXEMPLO    EXTCONFIG                44          1            0         11 15/10/2016 00:40:27
EXEMPLO    STAT_EXEMPLO             108          5            0        304 15/10/2016 00:40:27
EXEMPLO    SERVER_STAT               0          0            0          0 15/10/2016 00:40:27
EXEMPLO    TMP_TOP_SESSION           0          0            0          0 15/10/2016 00:40:28
EXEMPLO    TOP_SESSION               0          0            0          0 15/10/2016 00:40:28
EXEMPLO    TOP_SQL_TEXT              0          0            0          0 15/10/2016 00:40:28
EXEMPLO    TOP_SQL                   0          0            0          0 15/10/2016 00:40:28
EXEMPLO    LOG_TEMPO                33          5            0          5 15/10/2016 00:40:27
EXEMPLO    ESTATISTICA              75        244            0      20132 15/10/2016 00:40:27
EXEMPLO    DISPONIBILIDADE          19         20            0         26 15/10/2016 00:40:25
EXEMPLO    BACKUP                    0          0            0          0 15/10/2016 00:40:25
EXEMPLO    MENSAGEM                 86        622            0      40316 15/10/2016 00:40:27
EXEMPLO    INSTANCIA                 0         28            0          0 15/10/2016 00:40:27
EXEMPLO    ESQUEMA                  18         13            0       1766 15/10/2016 00:40:26
EXEMPLO    TABLESPACE               24         20            0       3269 15/10/2016 00:40:28
EXEMPLO    HIST_ARMAZENAME           0          0            0          0 15/10/2016 00:40:27
EXEMPLO    ARMAZENAMENTO             0          0            0          0 15/10/2016 00:40:25
EXEMPLO    SERVIDOR                  0         28            0          0 15/10/2016 00:40:27
EXEMPLO    CONFIG                   27          5            0          1 15/10/2016 00:40:25


3) Restauramos o schema todo. Antes, deletamos todas as estatísticas do schema.

-- Delete
exec DBMS_STATS.delete_schema_stats(ownname=>'EXEMPLO');

-- Import
exec DBMS_STATS.import_schema_stats(ownname=>'EXEMPLO',stattab=>'STAT_EXEMPLO',statown=>'EXEMPLO');

4) E conferimos de novo.
OWNER      TABLE_NAME      AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS   NUM_ROWS LAST_ANALYZED
---------- --------------- ----------- ---------- ------------ ---------- -------------------
EXEMPLO    EXTCONFIG                44          1            0         11 10/09/2016 23:02:50
EXEMPLO    STAT_EXEMPLO
EXEMPLO    SERVER_STAT               0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TMP_TOP_SESSION           0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TOP_SESSION               0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TOP_SQL_TEXT              0          0            0          0 10/09/2016 23:02:50
EXEMPLO    TOP_SQL                   0          0            0          0 10/09/2016 23:02:50
EXEMPLO    LOG_TEMPO                33          5            0          5 13/10/2016 23:00:13
EXEMPLO    ESTATISTICA              75        244            0      19539 12/10/2016 23:00:04
EXEMPLO    DISPONIBILIDADE          19         20            0         26 13/10/2016 23:00:11
EXEMPLO    BACKUP                    0          0            0          0 10/09/2016 23:02:50
EXEMPLO    MENSAGEM                 86        496            0      37974 10/10/2016 23:00:06
EXEMPLO    INSTANCIA                 0         28            0          0 13/10/2016 23:00:13
EXEMPLO    ESQUEMA                  18         13            0       1641 10/10/2016 23:00:05
EXEMPLO    TABLESPACE               24         13            0       2550 28/09/2016 23:00:03
EXEMPLO    HIST_ARMAZENAME           0          0            0          0 10/09/2016 23:02:50
EXEMPLO    ARMAZENAMENTO             0          0            0          0 10/09/2016 23:02:50
EXEMPLO    SERVIDOR                  0         28            0          0 13/10/2016 23:00:13
EXEMPLO    CONFIG                   27          5            0          1 10/09/2016 23:02:50

A tabela STAT_EXEMPLO está sem estatística pois quando criamos o backup ela ainda não existia.

Veja que são procedimentos extremamente simples e que, confesso, geralmente eu não executa. Até o dia que eu precisei e o backup automático não estava disponível.

Certo, tudo ok até aqui e tranquilo né. Esse é o método que eu gosto de utilizar pois tenho controle total na operação.

Ahhh, só lembrando. Se por acaso você tem as estatísticas em uma área de homologação que se mostra mais eficaz que as estatísticas na produção, como eu poderia migrar essas estatísticas?

Simples meu amigo, como o nosso querido amigo exp/imp (ou expdp/impdp se preferir).

exp \'/ as sysdba\' FILE=estat.dmp LOG=exp_estat.log TABLES="exemplo.STAT_EXEMPLO"

imp \'/ as sysdba\' FILE=estat.dmp LOG=imp_estat.log TABLES="exemplo.STAT_EXEMPLO" ignore=Y

E agora é só executar os procedimentos listados acima.

Beleza, e se eu não fiz o export das estatísticas. Não tem problema, o Oracle faz um backup dessas estatísticas e retém por um tempo.

Vamos utilizar o backup automático do Oracle então para ver como é.

1) Vamos verificar a retenção da coleta (por default, 31 dias) e a data mais antiga disponível para restore.

-- Default da retenção
select dbms_stats.get_stats_history_retention from dual;

GET_STATS_HISTORY_RETENTION
---------------------------
31

-- Coleta mais antiga disponível

select dbms_stats.get_stats_history_availability from dual;

GET_STATS_HISTORY_AVAILABILITY
----------------------------------------------------------------
13-SEP-16 11.18.16.343981000 PM -03:00

2) Vou coletar novamente as estatísticas do schema todo. E vou consultar a tabela DBA_TAB_STATS_HISTORY

select table_name, stats_update_time from DBA_TAB_STATS_HISTORY where table_name='MENSAGEM' order by 2;

TABLE_NAME      STATS_UPDATE_TIME
--------------- ------------------------------------
MENSAGEM        25-SEP-16 11.00.05.468877 PM -03:00
MENSAGEM        10-OCT-16 11.00.06.423578 PM -03:00
MENSAGEM        15-OCT-16 12.31.00.454985 AM -03:00
MENSAGEM        15-OCT-16 12.34.24.597019 AM -03:00
MENSAGEM        15-OCT-16 12.39.30.145758 AM -03:00
MENSAGEM        15-OCT-16 12.40.27.476614 AM -03:00
MENSAGEM        15-OCT-16 12.42.53.868798 AM -03:00
MENSAGEM        15-OCT-16 12.42.58.278735 AM -03:00

Essas acima são as coletas disponíveis para restore. E lembre-se como está a tabela nesse momento:

OWNER      TABLE_NAME      AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS   NUM_ROWS LAST_ANALYZED
---------- --------------- ----------- ---------- ------------ ---------- -------------------
EXEMPLO     MENSAGEM                 86        622            0      40324 15/10/2016 01:01:38

3) Vamos restaurar a estatística da tabela MENSAGEM, de "25-SEP-16 11.00.05.468877 PM -03:00".

exec DBMS_STATS.delete_table_stats(ownname=>'EXEMPLO',tabname=>'MENSAGEM');

exec dbms_stats.restore_table_stats('EXEMPLO', 'MENSAGEM','25-SEP-16 11.00.05.468877 PM -03:00');

4) E está lá, a estatística do dia 25/09.

OWNER      TABLE_NAME      AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS   NUM_ROWS LAST_ANALYZED
---------- --------------- ----------- ---------- ------------ ---------- -------------------
EXEMPLO     MENSAGEM                 86        496            0      29338 25/09/2016 23:00:05


Para verificar todas as operações realizadas na tabela, basta consultar a DBA_OPTSTAT_OPERATIONS .





col OPERATION  for a20
col TARGET     for a20
col START_TIME for a40
col END_TIME  for a40
col STATUS for a10
col JOB_NAME for a10

select 
    ID,
    OPERATION,
    TARGET,
    START_TIME,
    END_TIME,
    STATUS
from 
    DBA_OPTSTAT_OPERATIONS 
where 
    target like '%MENSAGEM%'
ORDER BY 
    START_TIME;

ID OPERATION              TARGET               START_TIME                               END_TIME                                 STATUS
---------- ---------------------- -------------------- ---------------------------------------- ---------------------------------------- ----------
2056 gather_table_stats     SISMON.MENSAGEM      15-OCT-16 12.30.58.665379 AM -03:00      15-OCT-16 12.31.02.537099 AM -03:00      COMPLETED
2057 import_table_stats     SISMON.MENSAGEM      15-OCT-16 12.34.23.087547 AM -03:00      15-OCT-16 12.34.25.265047 AM -03:00      COMPLETED
2069 delete_table_stats     SISMON.MENSAGEM      15-OCT-16 01.09.41.084232 AM -03:00      15-OCT-16 01.09.41.132820 AM -03:00      COMPLETED
2072 restore_table_stats    SISMON.MENSAGEM      15-OCT-16 01.13.00.186013 AM -03:00      15-OCT-16 01.13.00.219791 AM -03:00      COMPLETED
2073 restore_table_stats    SISMON.MENSAGEM      15-OCT-16 01.13.52.145885 AM -03:00      15-OCT-16 01.13.52.178049 AM -03:00      COMPLETED

É isso pessoal. Espero que ajude.

Abraço
Mario

sexta-feira, 14 de outubro de 2016

Conhecendo o mundo do tal do ODA - purgeODALog Tool

Olá pessoal.

Quem trabalha com o Oracle Database Appliance (ODA) já deve ter reparado como são gerados muitos arquivos de log, trace, etc.

É uma enormidade mesmo. E como na configuração do ODA o "/u01" vem com um tamanho relativamente pequeno, várias vezes chegamos a ter uma ocupação alta dessa área.

Mesmo configurando o ADRCI para fazer o purge dos arquivos de alert, trace, etc ainda temos vários outros tipos de arquivos gerados e que vão ficando por lá consumindo muito espaço. 

Para resolver isso, apesar de muitos DBA's não conhecerem, a Oracle oferece um script de limpeza muito simples de ser usado. 

Mas cuidado, como tudo tem seu lado negro da força, se errar na passagem de parâmetros você pode gerar sérios probemas já que o script é executado como root e ele limpa mesmo, sem piedade.

Informações detalhadas desse script vocês acham em "ODA (Oracle Database Appliance): Cleanup trace and log files Using purgeODALog Tool (Doc ID 2081655.1)".

Repare na mensagem muito importante do note:


AHAHHAH, ou seja, use por sua conta e risco e com muito cuidado.

Abaixo, vou mostrar um exemplo que uso esse script.

Primeiro é claro, faça o download do script no note acima. Eu salvo esse script no home do root, mas escolha onde achar melhor.

O uso pode ser individual ou com várias opções combinadas.

Usage:
purgeODALog
    [ -orcl ] | 
 [ -tfa ]  |
    [ -osw ]  | 
 [ -oak ]  |
    [ -extra '    ': | [, '     ':] ]
    [ -lsnr ]
    [ -dryrun ]
OPTIONS:
-orcl             Purge only GI/RDBMS logs & traces (Default 30 days)
-tfa               Purge only TFA repository older then # days (Default 30 days)
-osw             Purge only OSW archives older then # days (Default 30 days)
-oak              Purge only OAK logs and trace older then # days (Default 30 days)
-extra '':        Purge only files in user specified folders (Default 30 days)
-lsnr              It will force the cleanup of listeners log independently by the age
-dryrun         It will show the purge commands w/o execute them
-h                 Display this help and exit

Eu costumo usar assim, em alguns de meus ambientes:
1) Lista o que vai ser excluído, sem é claro efetuar a limpeza.
./purgeODALog -orcl 15 -tfa 15 -osw 15 -oak 15 -lsnr -dryrun 

2) Efetua a limpeza
./purgeODALog -orcl 15 -tfa 15 -osw 15 -oak 15 -lsnr

O final do log gerado é esse:
...
2016-10-13 09:58:45: I ... purging ALERT older than 15 days
2016-10-13 09:58:45: I ... purging INCIDENT older than 15 days
2016-10-13 09:58:45: I ... purging TRACE older than 15 days
2016-10-13 09:58:45: I ... purging CDUMP older than 15 days
2016-10-13 09:58:45: I ... purging HM older than 15 days
2016-10-13 09:58:45: I Purging TFA archives older then 15 days
2016-10-13 09:58:48: I Purging OSW archives older then 15 days
2016-10-13 09:58:48: I Purging OAK logs older then 15 days
2016-10-13 09:58:48: S Purging completed succesfully!

Veja o antes e depois do diretório onde a limpeza aconteceu:
-- Antes
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroupSys-LogVolU01
  97G   79G   14G  86% /u01
-- Depois
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroupSys-LogVolU01
  97G   63G   30G  68% /u01

Deixei separado aqui para mostrar a opção "extra". Ela é muito interessante, mas como disse acima devemos tomar muito cuidado.

Digamos que temos a pasta "/tmp/exemplo" e lá temos alguns arquivos e mais um outro diretório chamado "outrapasta".
[srv1 exemplo]$ su -

[srv1 exemplo]$ cd /tmp/exemplo

[srv1 exemplo]$ pwd
/tmp/exemplo

[srv1 exemplo]$ ls -lrht
-rw-r--r-- 1 oracle oinstall 0 Sep 14 10:16 z.txt
-rw-r--r-- 1 oracle oinstall 0 Sep 14 10:16 x.txt
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:16 y.txt
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:16 zzz.dmp
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:17 www.dmp
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:17 aaa.log
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:19 outrapasta
-rw-r--r-- 1 oracle oinstall 0 Sep 14 10:17 bbb.log
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:17 aaa.log
Vou passar para o script os parâmetros para:
1) Limpar "/tmp/exemplo" e deixar apenas arquivos com menos de 10 dias.
2) Limpar "/tmp/exemplo/outrapasta" e deixar apenas arquivos com menos de 20 dias.
Agora é só executar:
./purgeODALog -extra '/tmp/exemplo':10, '/tmp/exemplo/outrapasta':20

[srv1 exemplo]$ ls -lrht
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:16 y.txt
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:16 zzz.dmp
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:17 www.dmp
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:17 aaa.log
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:19 outrapasta
-rw-r--r-- 1 oracle oinstall 0 Oct 14 10:17 aaa.log

Repare que só restaram os arquivos de Outubro, ou seja, os que tem menos do que 10 e 20 dias respectivamente.

E claro, você pode agendar essas limpezas no crontab para automatizar o processo.

É isso pessoal, espero que ajude.

Abraço
Mario

quarta-feira, 12 de outubro de 2016

BUG: Oracle Cluster Health Monitor (CHM) using large amount of space

Fala pessoal...

Esses dias peguei um bug que nunca havia visto, por isso vou registrar aqui para todos.

Depois de um simples df -h, o resultado que recebo em um dos nodes do RAC 11g de um cliente é que meu "/oracle" (onde instalo os binários do GRID e RDBMS), estava com mais de 90% de ocupação.

Achei estranho e ao verificar minhas pastas, cheguei ao arquivo "crfclust.bdb" com quase 30G de tamanho.

-rw-r----- 1 root root 30G Oct  5 18:19 crfclust.bdb

Ao acessar o MOS, encontrei o note "Oracle Cluster Health Monitor (CHM) using large amount of space (more than default) (Doc ID 1343105.1)". Esse note está relacionado ao bug "BUG:10165314 - CHM/CRF/IPDOS REPOSITORY EXCEEDS 1GB AFTER ADD/REMOVE NODE OR FRESH INSTALL".

Só como curiosidade e bem rapidamente, o CHM coleta estatísticas (métricas de sistema) em real-time do SO. A coleta abrange memória, uso de SWAP, processos, I/O, etc. Essa coleta se dá através do "Cluster Health Monitor Service" - ora.crf. 

No note "Cluster Health Monitor (CHM) FAQ (Doc ID 1328466.1)", você encontra diversas informações muito mais detalhadas sobre esse serviço.

Voltando ao nosso problema, não podemos ficar com esse arquivo tão grande assim. O procedimento é simples para recriar o mesmo.

Esse procedimento pode ser feito com o cluster no ar, tranquilamente. E deve ser feito como ROOT.

1) Exporte as variáveis de ambiente:
        su -
export ORACLE_GRID=/oracle/app/11.2.0/grid

2) Execute o comando abaixo para confirmar o local do nosso arquivo
$ORACLE_GRID/bin/oclumon manage -get reppath

A saída esperada é essa:
CHM Repository Path = /oracle/app/11.2.0/grid/crf/db/srv1

Done

3) Vá até o diretório e identifique o arquivo
cd /oracle/app/11.2.0/grid/crf/db/srv1

ls -lrht crfclust.bdb
-rw-r----- 1 root root 30G Oct  5 18:19 crfclust.bdb

4) Vamos parar o serviço do CHM
$ORACLE_GRID/bin/crsctl stop res ora.crf -init
CRS-2673: Attempting to stop 'ora.crf' on 'srv1'

5) Remover o arquivo
rm -f crfclust.bdb

6) Iniciamos o serviço novamente
$ORACLE_GRID/bin/crsctl start res ora.crf -init
CRS-2672: Attempting to start 'ora.crf' on 'srv1'
CRS-2676: Start of 'ora.crf' on 'srv1' succeeded

Nesse momento, o arquivo "crfclust.bdb" será gerado novamente e a coleta será retomada. Além da perda das estatísticas, a exclusão desse arquivo não gera mais nenhum problema para o cluster.

That's it folks.

Espero que seja útil a vocês.

Abraço
Mario

segunda-feira, 10 de outubro de 2016

Duplicate sem acessar o Target e o Catalog

Olá pessoal.

Manja quando te pedem para criar uma base nova para desenvolvimento, simulação, etc. Tranquilo.

Vamos fazer um duplicate e resolvemos o assunto, certo? 

Só que o cara da infra fala que o duplicate não vai ser possível (nem o "comum", nem o active) já que o processo vai trafegar muitos dados pela rede, e o backup fica em uma rede separada e vai sentar o link se fizer, e blá blá blá.

Ok, então vamos copiar o backup para a máquina de destino, e fazemos um restore pensa o pequeno gafanhoto, nosso DBA. 

Opa um restore também complica, poque essa máquina é usada para um DataGuard do cliente, e ele já tem uma base chamada "BANCO49" lá, que é o mesmo nome claro da sua base de produção.

Claro, poderíamos usar um workaround (nome bonito né) e colocar o nosso DB_UNIQUE_NAME com o nome da nossa nova base e manter o DB_NAME com o nome original para "burlar" a restrição dos nomes.

Porém isso não é recomendável e não é legal também. Para resolver esse problema, desde a versão 11g temos uma alternativa legal e que nem todo mundo conhece. 

Podemos fazer um duplicate sem acessar o target.

No note "RMAN 11GR2 : DUPLICATE Without Target And Recovery Catalog Connection (Doc ID 874352.1)" temos o procedimento descrito que é muito simples e eu descrevo abaixo para vocês, com alguns acertos feitos por mim.

Na verdade o procedimento é o mesmo de uma duplicate comum, com apenas alguns acertos.

1) Os backuppieces devem ser copiados para a máquina de destino. No nosso exemplo, irei copiar para "/nfbackup/rman/BANCO49".

2) O procedimento funciona com o "DB_FILE_NAME_CONVERT", porém eu gosto de ter um pouco mais de controle sobre o processo, por isso eu uso o "SET NEWNAME".

3) Podemos usar o OMF (Oracle Managed Files) para definição dos locais dos datafiles. Eu particularmente gosto disso e uso sempre o "SET NEWNAME TO NEW". Sendo assim, devemos configurar:
*.db_create_file_dest                 = '/BANCO49/oradata/banco49/'
*.db_create_online_log_dest_1 = '/BANCO49/oradata/banco49/'
*.db_create_online_log_dest_2 = '/BANCO49/oradata/banco49/'

4) Todos os outros procedimentos para o duplicate de uma base comum devem ser observados.

Tudo isso pronto, vamos ao duplicate. Só que antes, mais algumas observações importantes:

1) Repare que iremos nos conectar somente a base auxiliar ("rman AUXILIARY /").
2) Podemos alocar quantos canais precisarmos, mas não esqueça somente no EE.
3) Esse duplicate somente funciona com o "UNTIL TIME".
4) O comando de duplicate também é um pouquinho diferente. 
5) O pulo do gato está na cláusula "BACKUP LOCATION '/local/do/backup' "

Todas as mudanças estão listadas abaixo.

rman AUXILIARY / << EOF
RUN
{
ALLOCATE AUXILIARY CHANNEL aux1 DEVICE TYPE DISK
                FORMAT '/nfbackup/rman/BANCO49/%d_%Y%M%D_%U.dat';
ALLOCATE AUXILIARY CHANNEL aux2 DEVICE TYPE DISK
                FORMAT '/nfbackup/rman/BANCO49/%d_%Y%M%D_%U.dat';
ALLOCATE AUXILIARY CHANNEL aux3 DEVICE TYPE DISK
                FORMAT '/nfbackup/rman/BANCO49/%d_%Y%M%D_%U.dat';
ALLOCATE AUXILIARY CHANNEL aux4 DEVICE TYPE DISK
                FORMAT '/nfbackup/rman/BANCO49/%d_%Y%M%D_%U.dat';
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 tempfile 1 to new;
set newname for tempfile 2 to new;
DUPLICATE DATABASE TO banco49
UNTIL TIME "TO_DATE('02/10/2016 10:00:00','DD/MM/YYYY HH24:MI:SS')"
LOGFILE
GROUP 1 ('/BANCO49/oradata/sml49/redo01.log') SIZE 300M REUSE,
GROUP 2 ('/BANCO49/oradata/sml49/redo02.log') SIZE 300M REUSE,
GROUP 3 ('/BANCO49/oradata/sml49/redo03.log') SIZE 300M REUSE
BACKUP LOCATION '/nfbackup/rman/BANCO49';
}
EXIT;
EOF

É isso, agora basta criar um arquivo dup.sh e deixar rodando em nohup.

Pode parecer besteira, mas já quebrou alguns galhos....

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...