Mostrando postagens com marcador Backup. Mostrar todas as postagens
Mostrando postagens com marcador Backup. Mostrar todas as postagens

terça-feira, 13 de fevereiro de 2018

RMAN - Simulando backup para fita

Se você não possui drive de fita mas quer simular um backup para fita ou mesmo realizar testes quando dizem que o problema está no RMAN e não no produto de terceiros como NetBackup, NetWorker, Data Protector e TSM, por exemplo, utilize a API oracle.disksbt que a Oracle fornece para simular o uso da MML (Media Management Library) fazendo com o que um diretório do servidor seja tratado igual a uma unidade de fita.

A API pode ser especificada na alocação do canal ou configurada nas parametrizações do RMAN onde será atribuída automaticamente ao canal quando um backup SBT_TAPE for executado inibindo a necessidade de alocar o canal com os parâmetros manualmente.

SBT_LIBRARY  : recebe a API que simula o uso da MML;
BACKUP_DIR   : recebe o diretório que serão gerados os arquivos de backup.

Na Alocação do canal:


[oracle@db3 ~]$ mkdir -p /orabackup/sbt
[oracle@db3 ~]$ rman target /

Recovery Manager: Release 12.2.0.1.0 - Production on Tue Feb 13 18:32:40 2018

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

connected to target database: CDB1 (DBID=913632474)

RMAN> RUN {
2> ALLOCATE CHANNEL CH01 DEVICE TYPE 'SBT_TAPE' PARMS="SBT_LIBRARY=oracle.disksbt, ENV=(BACKUP_DIR=/orabackup/sbt)";
3> BACKUP CURRENT CONTROLFILE;
4> }

using target database control file instead of recovery catalog
allocated channel: CH01
channel CH01: SID=56 device type=SBT_TAPE
channel CH01: WARNING: Oracle Test Disk API

Starting backup at 13-FEB-18
channel CH01: starting full datafile backup set
channel CH01: specifying datafile(s) in backup set
including current control file in backup set
channel CH01: starting piece 1 at 13-FEB-18
channel CH01: finished piece 1 at 13-FEB-18
piece handle=2jsr52rc_1_1 tag=TAG20180213T173331 comment=API Version 2.0,MMS Version 8.1.3.0
channel CH01: backup set complete, elapsed time: 00:00:01
Finished backup at 13-FEB-18

Starting Control File and SPFILE Autobackup at 13-FEB-18
piece handle=c-913632474-20180213-00 comment=API Version 2.0,MMS Version 8.1.3.0
Finished Control File and SPFILE Autobackup at 13-FEB-18
released channel: CH01

RMAN> exit


Recovery Manager complete.
[oracle@db3 ~]$ ls -lrt /orabackup/sbt/
total 49164
-rw-r--r--. 1 oracle oinstall 25165840 Feb 13 18:33 2jsr52rc_1_1
-rw-r--r--. 1 oracle oinstall 25165840 Feb 13 18:33 c-913632474-20180213-00
-rw-r--r--. 1 oracle oinstall     3360 Feb 13 18:33 Oracle_Disk_SBT_Catalog

 Nas parametrizações do RMAN:

[oracle@db3 ~]$ rman target /

Recovery Manager: Release 12.2.0.1.0 - Production on Tue Feb 13 18:43:04 2018

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

connected to target database: CDB1 (DBID=913632474)

RMAN> show all;

using target database control file instead of recovery catalog
RMAN configuration parameters for database with db_unique_name CDB1 are:
CONFIGURE RETENTION POLICY TO REDUNDANCY 1; # default
CONFIGURE BACKUP OPTIMIZATION OFF; # default
CONFIGURE DEFAULT DEVICE TYPE TO DISK; # default
CONFIGURE CONTROLFILE AUTOBACKUP ON; # default
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '%F'; # default
CONFIGURE DEVICE TYPE DISK PARALLELISM 1 BACKUP TYPE TO BACKUPSET; # default
CONFIGURE DATAFILE BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE ARCHIVELOG BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE MAXSETSIZE TO UNLIMITED; # default
CONFIGURE ENCRYPTION FOR DATABASE OFF; # default
CONFIGURE ENCRYPTION ALGORITHM 'AES128'; # default
CONFIGURE COMPRESSION ALGORITHM 'BASIC' AS OF RELEASE 'DEFAULT' OPTIMIZE FOR LOAD TRUE ; # default
CONFIGURE RMAN OUTPUT TO KEEP FOR 7 DAYS; # default
CONFIGURE ARCHIVELOG DELETION POLICY TO NONE; # default
CONFIGURE SNAPSHOT CONTROLFILE NAME TO '/orabin/app/oracle/product/12.2.0.1/dbhome_1/dbs/snapcf_cdb1.f'; # default

RMAN> CONFIGURE CHANNEL DEVICE TYPE 'SBT_TAPE' PARMS="SBT_LIBRARY=oracle.disksbt, ENV=(BACKUP_DIR=/orabackup/sbt)";

new RMAN configuration parameters:
CONFIGURE CHANNEL DEVICE TYPE 'SBT_TAPE' PARMS  "SBT_LIBRARY=oracle.disksbt, ENV=(BACKUP_DIR=/orabackup/sbt)";
new RMAN configuration parameters are successfully stored

RMAN> CONFIGURE DEFAULT DEVICE TYPE TO 'SBT_TAPE';

new RMAN configuration parameters:
CONFIGURE DEFAULT DEVICE TYPE TO 'SBT_TAPE';
new RMAN configuration parameters are successfully stored

RMAN> backup current controlfile format 'exemplo2.bkp';

Starting backup at 13-FEB-18
allocated channel: ORA_SBT_TAPE_1
channel ORA_SBT_TAPE_1: SID=59 device type=SBT_TAPE
channel ORA_SBT_TAPE_1: WARNING: Oracle Test Disk API
channel ORA_SBT_TAPE_1: starting full datafile backup set
channel ORA_SBT_TAPE_1: specifying datafile(s) in backup set
including current control file in backup set
channel ORA_SBT_TAPE_1: starting piece 1 at 13-FEB-18
channel ORA_SBT_TAPE_1: finished piece 1 at 13-FEB-18
piece handle=exemplo2.bkp tag=TAG20180213T174719 comment=API Version 2.0,MMS Version 8.1.3.0
channel ORA_SBT_TAPE_1: backup set complete, elapsed time: 00:00:01
Finished backup at 13-FEB-18

Starting Control File and SPFILE Autobackup at 13-FEB-18
piece handle=c-913632474-20180213-01 comment=API Version 2.0,MMS Version 8.1.3.0
Finished Control File and SPFILE Autobackup at 13-FEB-18

RMAN> exit


Recovery Manager complete.
[oracle@db3 ~]$ ls -lrt /orabackup/sbt/
total 98328
-rw-r--r--. 1 oracle oinstall 25165840 Feb 13 18:33 2jsr52rc_1_1
-rw-r--r--. 1 oracle oinstall 25165840 Feb 13 18:33 c-913632474-20180213-00
-rw-r--r--. 1 oracle oinstall 25165840 Feb 13 18:47 exemplo2.bkp
-rw-r--r--. 1 oracle oinstall 25165840 Feb 13 18:47 c-913632474-20180213-01
-rw-r--r--. 1 oracle oinstall     5600 Feb 13 18:47 Oracle_Disk_SBT_Catalog

 A nível de curiosidade, a API existe desde a versão 9i do Oracle database ¹.

Referências:

https://docs.oracle.com/cd/A84870_01/doc/server.816/a76990.pdf
https://docs.oracle.com/cd/B10500_01/server.920/a96566/wnrcmug.htm¹
https://docs.oracle.com/cd/B19306_01/backup.102/b14191/rcmconfg.htm#BRADV006
Mais informações →

terça-feira, 6 de fevereiro de 2018

ORA-19909: datafile 1 belongs to an orphan incarnation

Durante a realização de um duplicate from active database, fui notificado que o processo havia falhado durante o recover da base de dados apresentando o erro ORA-19909 conforme detalhamento (case de exemplo) a seguir.


RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 01/10/2017 16:42:19
RMAN-05501: aborting duplication of target database
RMAN-03015: error occurred in stored script Memory Script
ORA-00283: recovery session canceled due to errors
RMAN-11003: failure during parse/execution of SQL statement: alter database recover if needed
 start until change 226605 using backup controlfile
ORA-00283: recovery session canceled due to errors
ORA-19909: datafile 1 belongs to an orphan incarnation
ORA-01110: data file 1: '/u01/app/oracle/oradata/dbtst/system.dbf'

Como a liberação da base era importante para a continuidade dos negócios, verifiquei o incarnation do ambiente produtivo e realizei o reset do incarnation do ambiente auxiliary para a mesma incarnação do produção, conclui o recover e demais etapas necessárias.

DBPROD:

RMAN> list incarnation;


List of Database Incarnations
DB Key  Inc Key DB Name  DB ID            STATUS  Reset SCN  Reset Time
------- ------- -------- ---------------- --- ---------- ----------
1       1       DBPRD    3274873056       CURRENT 1          30-OCT-17

DBTST:
RMAN> list incarnation;

using target database control file instead of recovery catalog

List of Database Incarnations
DB Key  Inc Key DB Name  DB ID            STATUS  Reset SCN  Reset Time
------- ------- -------- ---------------- --- ---------- ----------
1       1       DBPRD    3274873056       PARENT  1          30-OCT-17
2       2       DBPRD    3274873056       ORPHAN  221767     05-NOV-17
4       4       DBPRD    3274873056       ORPHAN  222025     05-NOV-17
3       3       DBPRD    3274873056       CURRENT 222452     05-NOV-17

RMAN> reset database to incarnation 1;

database reset to incarnation 1

RMAN> list incarnation of database;


List of Database Incarnations
DB Key  Inc Key DB Name  DB ID            STATUS  Reset SCN  Reset Time
------- ------- -------- ---------------- --- ---------- ----------
1       1       DBPRD    3274873056       CURRENT 1          30-OCT-17
2       2       DBPRD    3274873056       ORPHAN  221767     05-NOV-17
4       4       DBPRD    3274873056       ORPHAN  222025     05-NOV-17
3       3       DBPRD    3274873056       ORPHAN  222452     05-NOV-17



<.. recover e demais processos ..>

Na semana seguinte fui notificado da mesma ocorrência, fiz o mesmo procedimento para liberação do ambiente mas também fui analisar o motivo que gerava o erro para resolvê-lo de modo que não fosse necessário uma nova ação manual durante o próximo processo de duplicate.

Examinando o log de alertas do banco (alertlog), podemos constatar a ocorrência de vários "Setting recovery target incarnation to N" as 16:42:14 que represetam as mudanças de incarnation durante o processo de recover. 


[oracle@tst1 /]$ vi $ORACLE_BASE/diag/rdbms/dbtst/dbtst/trace/alert_dbtst.log
..
..
Tue Jan 10 16:41:29 2017
alter database mount
Set as converted control file due to db_unique_name mismatch
Changing di2dbun from DBPRD to DBTST
Successful mount of redo thread 1, with mount id 3249055801
Database mounted in Exclusive Mode
Lost write protection disabled
Completed: alter database mount
RMAN DUPLICATE: Errors in krbm_getDupCopy
Errors in file /u01/app/oracle/diag/rdbms/dbtst/dbtst/trace/dbtst_ora_14094.trc:
ORA-19625: error identifying file /u01/app/oracle/oradata/dbtst/system.dbf
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3
RMAN DUPLICATE: Errors in krbm_getDupCopy
Errors in file /u01/app/oracle/diag/rdbms/dbtst/dbtst/trace/dbtst_ora_14094.trc:
ORA-19625: error identifying file /u01/app/oracle/oradata/dbtst/sysaux.dbf
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3
RMAN DUPLICATE: Errors in krbm_getDupCopy
Errors in file /u01/app/oracle/diag/rdbms/dbtst/dbtst/trace/dbtst_ora_14094.trc:
ORA-19625: error identifying file /u01/app/oracle/oradata/dbtst/undotbs1.dbf
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3
Tue Jan 10 16:42:14 2017
Setting recovery target incarnation to 2 <========
Setting recovery target incarnation to 2 <========
Setting recovery target incarnation to 3 <========
Setting recovery target incarnation to 3 <========
Tue Jan 10 16:42:14 2017
Switch of datafile 1 complete to datafile copy
  checkpoint is 226585
Switch of datafile 2 complete to datafile copy
  checkpoint is 226597
Switch of datafile 3 complete to datafile copy
  checkpoint is 226594
alter database recover datafile list clear
Completed: alter database recover datafile list clear
alter database recover datafile list
 1 , 2 , 3
Completed: alter database recover datafile list
 1 , 2 , 3
alter database recover if needed
 start until change 226605 using backup controlfile
Media Recovery Start
Serial Media Recovery started
Warning: Recovery target destination is in a sibling branch
of the controlfile checkpoint. Recovery will only recover
changes to datafiles.
Datafile 1 (ckpscn 226585) is orphaned on incarnation#=1
Media Recovery failed with error 19909
ORA-283 signalled during: alter database recover if needed
 start until change 226605 using backup controlfile

Um pouco antes destas mensagens de mudança de incarnation podemos ver a ocorrência de outros erros juntamente com o nome de um arquivo de rastreamento (.trc). Abrindo o arquivo e pesquisando por incarnation podemos ver que eles ocorreram logo após a leitura de alguns archives pois neles foram identificados mudanças no incarnation deste database em decorrência de outras cargas com open resetlogs (sempre que ocorrer um RESETLOGS uma nova incarnation será gerada).

[oracle@tst1 /]$ vi /u01/app/oracle/diag/rdbms/dbtst/dbtst/trace/dbtst_ora_14094.trc
..
..
*** 2017-01-10 16:42:14.546
*** ACTION NAME:(0000023 STARTED124) 2017-01-10 16:42:14.546

New incarnation branch detected in ArchiveLog, filename /oraarchive/DBTST/archivelog/2017_11_05/dbtst_1_1_959263858.arc
Inspection of file changed rdi from 1 to 2
New incarnation branch detected in ArchiveLog, filename /oraarchive/DBTST/archivelog/2017_11_05/dbtst_1_1_959265418.arc
Inspection of file changed rdi from 2 to 3
New incarnation branch detected in ArchiveLog, filename /oraarchive/DBTST/archivelog/2017_11_05/dbtst_1_1_959264815.arc

Para solucionar o "problema" basta mover/remover os archives indicados (possivelmente já desnecessários) ou apontar a FRA (Fast Recovery Area) para outro destino onde não exitam archives com o mesmo dbid, caso não esteja sendo utilizado a recovery area basta mudar o apontamento do log_archive_dest_1, por exemplo.

Mas por que o processo de recover lê estes archives com outras incarnações?

A partir da versão 11.2.0.3 o comando foi alterado para catalogar todos os arquivos(archives) abaixo do local onde foi configurado a recovery area ou log_archive_dest_N do banco auxiliary, desta forma o "catalog clone start with +DISKGROUP" lê e cataloga todos os arquivos abaixo do diretório que contenham o mesmo DBID da base que está sendo clonada.

Este cenário ocorre normalmente nos casos em que:

FRA está configurada:

*.db_recovery_file_dest='+RECO1'


log_archive_dest_N utilizando LOCATION=+DISKGROUP:

*.log_archive_dest_1='LOCATION=+RECO1'


Para evitar o erro reaponte para outro destino conforme supracitado:

*.db_recovery_file_dest='+RECOC4'
*.log_archive_dest_1='LOCATION=+RECOC4/newdb'

contents of Memory Script:
{
  backup as copy reuse
  archivelog like  "+RECOC4/luz/archivelog/2014_07_18/thread_1_seq_1742.2339.853243229" auxiliary format "+RECOC4/newdb/1_1742_847627148.dbf"   ;
  catalog clone archivelog  "+RECOC4/newdb/1_1742_847627148.dbf";   <======== é catalogado apenas o archive copiado
  switch clone datafile all;


NOTA:
No cenário de exemplo acima copiei os archives com novas incarnações para dentro do diretório da base DBTST para simular o problema.

[oracle@tst1 /]$ cp /oraarchive/DBPRD/archivelog/2017_11_05/* /oraarchive/DBTST/archivelog/2017_11_05/

e durante o recover:

contents of Memory Script:
{
   backup as copy reuse
   archivelog like  "/oraarchive/DBPRD/archivelog/2017_01_10/o1_mf_1_14_d7bbr54c_.arc" auxiliary format
 "/oraarchive/DBTST/archivelog/2017_01_10/o1_mf_1_14_%u_.arc"   ;
   catalog clone recovery area; <======== catalogando todos os arquivos abaixo da FRA
   switch clone datafile all;
}

Referências:

RMAN-11003 ORA-283 ORA-19909 during duplicate database (Doc ID 1913573.1)
Manual Completion of a Failed RMAN Backup based Duplicate (Doc ID 360962.1)
Mais informações →

quarta-feira, 12 de julho de 2017

Importando catálogos RMAN - IMPORT CATALOG

Possuir uma política de backup e recuperação segura e eficaz é primordial para a sustentação de negócios e tecnologias. Quando se fala de backup e recovery no Oracle Database claramente se fala de RMAN (Recovery Manager), ferramenta criada e fornecida pela Oracle que proporciona diferentes opções de backup, restauração e recuperação dos dados.

Uma das boas práticas relacionadas ao RMAN é a utilização do catálogo de recuperação, seja para proporcionar uma melhor organização e centralização dos metadados de backups como para aumentar o tempo de retenção dos históricos de backups em relação ao control file, armazenamento de scripts RMAN e sincronização de backups Primary & Standby.

Frente a boa prática de utilização do catálogo RMAN, a Oracle introduziu a partir da versão 11g uma nova feature (característica) chamada IMPORT CATALOG. O comando IMPORT CATALOG simplesmente permite a importação dos metadados de um catálogo de recuperação para dentro de outro catálogo de recuperação.

Anterior a versão 11g era muito comum a existência de vários catálogos de recuperação pelo fato de não haver um controle do que cada administrador do catálogo poderia fazer ou ver, permitindo assim que um comando indevido ou mesmo malicioso destruísse o catálogo de outras bases de dados.


Junto com a versão 11g foi introduzido também o Virtual Private Catalog, desta forma, tornava-se agora possível restringir o que cada administrador do catálogo poderia fazer ou ver, com isso a centralização dos metadados de backups dentro de um único catálogo se tornou mais comum e atrativa.

Toda a sintaxe e utilização do comando IMPORT CATALOG é bem simples:


connectStringSpec
Especifica a string de conexão com o catálogo de recuperação de onde se quer importar os metadados.
DBID <integer>
Especifica a lista de DBIDs que se quer importar do antigo catálogo para o novo catálogo.
Quando não for especificado, todos os DBIDs serão copiados (migrados) para o novo catálogo e descatalogados do antigo catálogo (default).
DB_NAME <database_name>
Especifica a lista de databases que se quer importar do antigo catálogo para o novo catálogo.
Quando não for especificado, todos os databases serão copiados (migrados) para o novo catálogo e descatalogados do antigo catálogo (default).
NO UNREGISTER
Se especificado, força o RMAN a manter os metadados importados tanto no catálogo de origem (antigo) quanto no catálogo de destino (novo).

Alguns casos de uso demonstrando diferentes formas de utilização do IMPORT CATALOG:

Catalogo atual (origem):



[oracle@tst1 ~]$ rman catalog rman/rman

Recovery Manager: Release 11.2.0.4.0 - Production on Thu Jun 29 21:07:56 2017

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

connected to recovery catalog database

RMAN> list db_unique_name all;


List of Databases
DB Key  DB Name  DB ID            Database Role    Db_unique_name
------- ------- ----------------- ---------------  ------------------
661     DBPROD   1035714218       PRIMARY          DBPROD
361     UTF      1300612639       PRIMARY          UTF
1       DBTST    3113417416       PRIMARY          DBTST


 Catalogo destino (novo):



[oracle@tst1 ~]$ rman catalog rman/rman

Recovery Manager: Release 11.2.0.4.0 - Production on Thu Jun 29 21:08:25 2017

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

connected to recovery catalog database

RMAN> list db_unique_name all;


RMAN> IMPORT CATALOG rman@CATALOGO DB_NAME=UTF;

Starting import catalog at 29-JUN-17
source recovery catalog database Password:
connected to source recovery catalog database
import validation complete
database unregistered from the source recovery catalog
Finished import catalog at 29-JUN-17

RMAN> list db_unique_name all;


List of Databases
DB Key  DB Name  DB ID            Database Role    Db_unique_name
------- ------- ----------------- ---------------  ------------------
704     UTF      1300612639       PRIMARY          UTF

RMAN> IMPORT CATALOG rman@CATALOGO DBID=3113417416;

Starting import catalog at 29-JUN-17
source recovery catalog database Password:
connected to source recovery catalog database
import validation complete
database unregistered from the source recovery catalog
Finished import catalog at 29-JUN-17

RMAN> IMPORT CATALOG rman@CATALOGO NO UNREGISTER;

Starting import catalog at 29-JUN-17
source recovery catalog database Password:
connected to source recovery catalog database
import validation complete
Finished import catalog at 29-JUN-17

RMAN> list db_unique_name all;


List of Databases
DB Key  DB Name  DB ID            Database Role    Db_unique_name
------- ------- ----------------- ---------------  ------------------
2565    DBPROD   1035714218       PRIMARY          DBPROD
704     UTF      1300612639       PRIMARY          UTF
1123    DBTST    3113417416       PRIMARY          DBTST


 Referências:

https://docs.oracle.com/database/121/RCMRF/rcmsynta026.htm#RCMRF198
https://docs.oracle.com/database/121/BRADV/rcmmaint.htm#BRADV8007
Mais informações →

quinta-feira, 13 de outubro de 2016

Formatando um bloco corrompido que não faz parte de um segmento

Durante a execução de um backup full database(RMAN) obtive o erro ORA-19566 informando a identificação de blocos corrompidos em um datafile. 

ORA-19566: exceeded limit of 0 corrupt blocks for file /oradata01/oracle/orcl/indices/TBS_INDICES40.dbf

Abrindo o alertlog do banco de dados temos um detalhamento do ocorrido, onde aponta inclusive qual objeto utiliza este bloco.

Mon Oct 03 10:35:02 BRT 2016
Hex dump of (file 439, block 299712) in trace file /home/oracle/home/admin/orcl/udump/orcl_ora_25903.trc
Corrupt block relative dba: 0x6dc492c0 (file 439, block 299712)
Fractured block found during buffer read
Data in bad block:
 type: 6 format: 2 rdba: 0x6dc492c0
 last change scn: 0x0040.47e5f156 seq: 0x1 flg: 0x04
 spare1: 0x0 spare2: 0x0 spare3: 0x0
 consistency value in tail: 0x00c351d5
 check value in block header: 0xd7cb
 computed block checksum: 0x9ae3
Reread of rdba: 0x6dc492c0 (file 439, block 299712) found same corrupted data
Mon Oct 03 10:35:02 BRT 2016
Corrupt Block Found
         TSN = 25, TSNAME = TBS_INDICES
         RFN = 439, BLK = 299712, RDBA = 1841599168
         OBJN = 60476, OBJD = 60476, OBJECT = IDX_TEST_FK_I, SUBOBJECT =
         SEGMENT OWNER = PRD, SEGMENT TYPE = Index Segment
Após realizar o rebuild do indice, executei um DBV (DBVERIFY) sobre o arquivo de dados e além do bloco 299712, foram identificados mais 2 blocos corrompidos (299713 e 299714)

Para asber mais sobre o DBV, clique aqui.

Banco=orcl-> dbv file=/oradata01/oracle/orcl/indices/TBS_INDICES40.dbf

DBVERIFY: Release 10.2.0.5.0 - Production on Tue Oct 4 08:29:29 2016

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

DBVERIFY - Verification starting : FILE = /oradata01/oracle/orcl/indices/TBS_INDICES40.dbf
Page 299712 is influx - most likely media corrupt
Corrupt block relative dba: 0x6dc492c0 (file 439, block 299712)
Fractured block found during dbv:
Data in bad block:
 type: 6 format: 2 rdba: 0x6dc492c0
 last change scn: 0x0040.47e5f156 seq: 0x1 flg: 0x04
 spare1: 0x0 spare2: 0x0 spare3: 0x0
 consistency value in tail: 0x00c351d5
 check value in block header: 0xd7cb
 computed block checksum: 0x9ae3

Page 299713 is marked corrupt
Corrupt block relative dba: 0x6dc492c1 (file 439, block 299713)
Bad header found during dbv:
Data in bad block:
 type: 231 format: 2 rdba: 0x001bfcf8
 last change scn: 0x006c.3710b7ec seq: 0xb5 flg: 0x1c
 spare1: 0x19 spare2: 0x0 spare3: 0x65
 consistency value in tail: 0x3f8303ce
 check value in block header: 0x3903
 computed block checksum: 0x0

Page 299714 is marked corrupt
Corrupt block relative dba: 0x6dc492c2 (file 439, block 299714)
Bad header found during dbv:
Data in bad block:
 type: 231 format: 2 rdba: 0x001bfd08
 last change scn: 0x0048.3710b7ec seq: 0x6d flg: 0x6c
 spare1: 0x19 spare2: 0x0 spare3: 0x5301
 consistency value in tail: 0x94410601
 check value in block header: 0x1389
 computed block checksum: 0x4162



DBVERIFY - Verification complete

Total Pages Examined         : 408832
Total Pages Processed (Data) : 0
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 405595
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 668
Total Pages Processed (Seg)  : 0
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 2566
Total Pages Marked Corrupt   : 3
Total Pages Influx           : 1
Highest block SCN            : 1749727541 (64.1749727541)
Voltei na base de dados e verifiquei se havia mais algum segmento utilizando estes blocos:

SQL> SELECT tablespace_name, segment_type, owner, segment_name FROM dba_extents WHERE file_id = 439 and 299712 between block_id AND block_id + blocks - 1 ;

no rows selected

SQL> SELECT tablespace_name, segment_type, owner, segment_name FROM dba_extents WHERE file_id = 439 and 299713 between block_id AND block_id + blocks - 1 ;

no rows selected

SQL> SELECT tablespace_name, segment_type, owner, segment_name FROM dba_extents WHERE file_id = 439 and 299714 between block_id AND block_id + blocks - 1 ;

no rows selected

Nenhum segmento identificado nestes blocos!

Pois bem, já que não temos nenhum segmento, precisamos formatar este blocos.

Primeiramente identificamos a qual tablespace pertence o arquivo de dados:

SQL>  select tablespace_name from dba_data_files where file_name='/oradata01/oracle/orcl/indices/TBS_INDICES40.dbf';

TABLESPACE_NAME
------------------------------
TBS_INDICES

Agora criamos uma tabela abaixo desta tablespace, como no exemplo a seguir:

create table prd.fix_corrupt ( 
n number, 
c varchar2(4000) 
) PCTFREE 95 tablespace TBS_INDICES;

Utilizei PCTFREE 95 para que posteriormente seja necessário a inserção de menos registros até chegarmos ao bloco corrompido. 

Criado a tabela, vamos criar uma trigger para abortar o processo se inserção quando o bloco corrompido for utilizado e nos informar em tela.

CREATE OR REPLACE TRIGGER corrupt_trigger 
  AFTER INSERT ON prd.fix_corrupt
  REFERENCING OLD AS p_old NEW AS new_p 
  FOR EACH ROW 
DECLARE 
  corrupt EXCEPTION; 
BEGIN 
  IF (dbms_rowid.rowid_block_number(:new_p.rowid)=299712)
 and (dbms_rowid.rowid_relative_fno(:new_p.rowid)=439) THEN 
     RAISE corrupt; 
  END IF; 
EXCEPTION 
  WHEN corrupt THEN 
     RAISE_APPLICATION_ERROR(-20000, 'Corrupt block has been formatted'); 
END; 
/

Na trigger informei rowid_relative_fno(:new_p.rowid)=439 que é meu file_id e o dbms_rowid.rowid_block_number(:new_p.rowid)=299712 que é meu bloco corrompido.

O próximo passo é identificar o tamanho do extent corrompido para que possamos alocá-lo, desta forma realizamos a seguinte consulta na dba_free_space informando o file_id e o numero do bloco, no meu caso 299712.

SQL> Select BYTES from dba_free_space where file_id=439 and 299712 between block_id and block_id + blocks -1;

     BYTES
----------
  67108864

Neste caso é 64K (67108864/1024/1024), assim é possivel alocar o extent da seguinte forma:

alter table prd.fix_corrupt
allocate extent (DATAFILE '/oradata01/oracle/orcl/indices/TBS_INDICES40.dbf' SIZE 64K);


Como não sabemos se existem muitos extents de 64K livres no datafile, podemos utilizar o seguinte loop para alocar os extents:

**Detalhe** 

Antes de executar o comando verificar o maxsize do datafile, ou seja, qual o tamanho maximo que o datafile pode atingir, pois se todos os extents lives tiverem o mesmo tamanho ele pode crescer o datafile até chegar a este limite.

SQL> select bytes/1024/1024, maxbytes/1024/1024 from dba_data_files where file_name='/oradata01/oracle/orcl/indices/TBS_INDICES40.dbf';

BYTES/1024/1024 MAXBYTES/1024/1024
--------------- ------------------
           3194               3500

Meu datafile neste momento tem 3.194 MB e pode chegar até 3.500 MB Executando o loop para alocar os extents:

SQL> BEGIN
for i in 1..1000000 loop
EXECUTE IMMEDIATE 'alter table prd.fix_corrupt allocate extent (DATAFILE '||'''/oradata01/oracle/orcl/indices/TBS_INDICES40.dbf''' ||'SIZE 64K) ';
end loop;
end ;
/  2    3    4    5    6
BEGIN
*
ERROR at line 1:
ORA-01653: unable to extend table PRD.FIX_CORRUPT by 128 in tablespace
TBS_INDICES
ORA-06512: at line 3

Como podemos ver acima ele alocou extents até estourar o datafile, vamos ver agora o tamanho.

SQL> select bytes/1024/1024, maxbytes/1024/1024 from dba_data_files where file_name='/oradata01/oracle/orcl/indices/TBS_INDICES40.dbf';

BYTES/1024/1024 MAXBYTES/1024/1024
--------------- ------------------
           3500               3500

Como ele estava com maxsize de 3500MB o datafile parou de crescer quando chegou a este valor, se ele fosse de 30G, por exemplo, possivelmente ele iria alocar toda esta área o que não seria necessário.. sempre observar este fator antes de executar o loop! se for necessário reduzir o maxsize e após as alocações voltar o valor antigo. 

Agora vamos verificar se os blocos estão sendo utilizados pela tabela criada:

SQL> set lines 200
SQL> col segment_name for a70
SQL> select segment_name, segment_type, owner from dba_extents where file_id = 439 and 299712 between block_id and block_id + blocks -1;

SEGMENT_NAME                                                           SEGMENT_TYPE       OWNER
---------------------------------------------------------------------- ------------------ ------------------------------
FIX_CORRUPT                                                            TABLE              PRD

SEGMENT_NAME                                                           SEGMENT_TYPE       OWNER
---------------------------------------------------------------------- ------------------ ------------------------------
FIX_CORRUPT                                                            TABLE              PRD

SQL> select segment_name, segment_type, owner from dba_extents where file_id = 439 and 299714 between block_id and block_id + blocks -1;

SEGMENT_NAME                                                           SEGMENT_TYPE       OWNER
---------------------------------------------------------------------- ------------------ ------------------------------
FIX_CORRUPT                                                            TABLE              PRD

Visto que os blocos listados como corrompidos estão sendo alocados pela tabela criada, vamos inserir registros até que o bloco seja preenchido e formatado. Para isto iremos utilizar um loop de inserção até que a mensagem gerada pela trigger seja exibida na tela:


Begin
  FOR i IN 1..1000000000 loop
    for j IN 1..1000 loop
      Insert into prd.fix_corrupt VALUES(i,'x');
    end loop;
    commit;
  END LOOP;
END; 
/

Begin
*
ERROR at line 1:
ORA-20000: Corrupt block has been formatted
ORA-06512: at "SYS.CORRUPT_TRIGGER", line 10
ORA-04088: error during execution of trigger 'SYS.CORRUPT_TRIGGER'
ORA-06512: at line 4

Agora executamos um novo DBV para verificar se todos os blocos foram formatados:

Banco=orcl-> dbv file=/oradata01/oracle/orcl/indices/TBS_INDICES40.dbf

DBVERIFY: Release 10.2.0.5.0 - Production on Tue Oct 4 09:31:26 2016

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

DBVERIFY - Verification starting : FILE = /oradata01/oracle/orcl/indices/TBS_INDICES40.dbf


DBVERIFY - Verification complete

Total Pages Examined         : 448000
Total Pages Processed (Data) : 80471
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 358990
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 1068
Total Pages Processed (Seg)  : 0
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 7471
Total Pages Marked Corrupt   : 0
Total Pages Influx           : 0
Highest block SCN            : 1758325239 (64.1758325239)
Banco=orcl->

Estando o valor de "Total Pages Marked Corrupt" = 0 ficou tudo OK, caso ele ainda indique a presença de blocos corrompidos e sem segmentos basta recriar a trigger informando o novo bloco, reexecutar o insert e ao final o DBV até que o valor de blocos marcados como corrompidos seja 0.

Ao termino do processo remover a tabela criada:

SQL> drop table prd.fix_corrupt;

Table dropped.

Referência: 
https://docs.oracle.com/cd/B28359_01/backup.111/b28270/rcmvalid.htm
Mais informações →

segunda-feira, 12 de agosto de 2013

Oracle Wait Interface (OWI) – Por onde começar?

Embora introduzido no Oracle 7, o Oracle Wait Interface (OWI) continua sendo hoje uma ferramenta inestimável para os DBAs, através dela é possível obter informações que ajudam na solução de problemas e na busca de melhorias na performance de todo o ambiente de banco de dados.

O Oracle Wait Interface nada mais é que uma coleção de visões dinâmicas de performance e arquivo de rastreamento SQL(SQL trace file) que promovem estatísticas de desempenho sobre gargalos de um processo mais conhecidas como eventos de espera(wait events) ou sintomas.

Utilizando o OWI podemos identificar todos os gargalos de espera que incidiram desde o inicio ao termino de um processo, incluindo esperas por operações de I/O, locks, network, dentre outros. Proporcionando um direcionamento para a solução ou redução do impacto causado pelo gargalo de espera.

Atualmente na versão 12.1.0.1 contamos com 1.567 eventos de espera(wait events), diferentemente de seus antecessores. O Oracle 11.2.0.3 por exemplo, conta com 1.152 eventos de espera, na versão 10g pouco mais de 800 enquanto na versão 7.3 contava com apenas 104 eventos de espera. (A quantidade de eventos de espera depende da versão, configuração e opções instaladas no RDBMS).


Wait Events são as condições em que uma sessão está esperando que algo aconteça. Elas podem ser a nível de sistema, onde afeta todo o banco de dados ou a nível de sessão em que afeta uma única atividade de usuário no banco de dados.

Conforme documentação Oracle, os wait events são elencados em três visões dinâmicas de performance:

V$SESSION_WAIT exibe os eventos para os quais as sessões acabaram de concluir ou estão esperando.

V$SYSTEM_EVENT exibe o número total de tempo que todas as sessões tem aguardado por eventos de espera.

V$SESSION_EVENT é semelhante ao V$SYSTEM_EVENT, mas mostra todas as esperas para cada sessão.

Todo evento de espera pertence a uma classe de espera(Wait Class). Através da visão V$EVENT_NAME podemos identificar os tipos de classe de espera e os tipos de eventos bem como o que representam os parâmetros(PARAMETER) P1,P2 e P3 da V$SESSION_WAIT igualmente como as colunas P1TEXT, P2TEXT e P3TEXT desta mesma view.

-- Lista das classes de espera
 
SQL> select distinct wait_class from v$event_name;
 
WAIT_CLASS
----------------------------------------------------------------
User I/O
Application
Network
Concurrency
Administrative
Configuration
Scheduler
Cluster
Other
Idle
System I/O
Commit
 
12 rows selected.
 
-- Detalhes de um evento:
 
SQL> set lines 190
SQL> col NAME for a40
SQL> col WAIT_CLASS for a20
SQL> col PARAMETER1 for a10
SQL> col PARAMETER2 for a15
SQL> col PARAMETER3 for a15
SQL> select * from v$event_name where name='db file scattered read';
 
    EVENT#   EVENT_ID NAME                                     PARAMETER1 PARAMETER2      PARAMETER3      WAIT_CLASS_ID WAIT_CLASS# WAIT_CLASS
---------- ---------- ---------------------------------------- ---------- --------------- --------------- ------------- ----------- --------------------
       118  506183215 db file scattered read                   file#      block#          blocks             1740759767           8 User I/O


A lista abaixo descreve brevemente cada classe:

Administrative 

Espera resultante de comandos administrativos (DBA). Por exemplo, um rebuild de índice.

Application

Espera resultante do código da aplicação do usuário. Por exemplo, lock a nível de linha ou comando explícito de lock.

Cluster 

Espera relacionada aos recursos do Real Application Clusters (RAC). Por exemplo, ‘gc cr block busy’.

Commit 

Esta classe cotém apenas um evento de espera. ‘log file sync’ – espera para o redolog confirmar um commit.

Concurrency 

Espera por recursos internos do banco de dados. Por exemplo, latches.

Configuration 

Espera causada por uma configuração inadequada do banco de dados ou recursos da instance. Por exemplo, mal dimensionamento do tamanho dos log file, shared pool size.

Idle 

Indica que a sessão está inativa, esperando para trabalhar. Por exemplo, ‘SQL*Net message from client’.

Network 

Espera relacionada a eventos de rede. Por exemplo, ‘SQL*Net more data to dblink’.

Other 

Esperas que normalmente não devem ocorrem em um sistema. Por exemplo, ‘wait for EMON to spawn’)

Scheduler 

Espera relacionada ao gerenciamento de recursos. Por exemplo, ‘resmgr: become active’. 

System I/O 

Espera por background process I/O. Por exemplo, DBWR wait for ‘db file parallel write’) 

User I/O

Espera por user I/O. Por exemplo ‘db file sequential read’.

Ao longo das versões, o Oracle database ganhou outras visões de desempenho que oferecem novas informações de espera por várias perspectivas incluindo features de apoio para atender limitações do OWI como:


  • ASH (Active Session History), coleta a todo segundo informações correntes de cada sessão. Seria um join da V$SESSION com a V$SESSION_WAIT porem com dados históricos das sessões ativas. 
  • AWR (Automatic Workload Repository), oferece o histórico de todas as atividades das sessões. Seu tempo de retenção default é 8 dias mas pode ser ajustado conforme necessidade. Os dados capturados podem ser acessados através de views (DBA_HIST_*) e pelo AWR report facilmente acessado utilizando o Oracle Enterprise Manager. 
  • ADDM (Automatic Database Diagnostic Monitor), faz uma análise automática dos dados capturados(snapshots) dentro do AWR e oferece recomendações de melhorias. 
Algumas outras views que complementam o OWI:

V$SYSTEM_WAIT_CLASS
V$SESSION_WAIT_CLASS
V$SESSION_WAIT_HISTORY
V$EVENT_HISTOGRAM
V$EVENTMETRIC
V$SERVICE_EVENT
V$SERVICE_WAIT_CLASS
V$ACTIVE_SESSION_HISTORY
V$WAITCLASSMETRIC
V$SESSTAT
V$STATNAME
V$SYSSTAT
V$SESS_TIME_MODEL
V$SYS_TIME_MODEL
DBA_HIST_EVENT_NAME
DBA_HIST_ACTIVE_SESS_HISTORY
DBA_HIST_BG_EVENT_SUMMARY
DBA_HIST_WAITCLASSMET_SUMMARY

Outro componente que faz parte do OWI (citado anteriormente) é o extended SQL Trace:

De forma simplificada, o rastreamento SQL (trace) é um “monitoramento” que pode ser habilitado sobre uma sessão/processo para identificarmos os comandos executados, tempos e gargalos para cada SQL, o que permite diagnosticar problemas de performance.

É possível habilitar um trace de diversas formas e com diferentes níveis de coleta:

Nível 0 Trace desabilitado. Igual ao SQL_TRACE = FALSE.
Nível 1 Informações de rastreamento SQL padrão (SQL_TRACE = TRUE). Este é o nível default.
Nível 4 Informações de rastreamento SQL + valores das bind variables.
Nível 8 Informações de rastreamento SQL + informações de wait events.
Nível 12 Informações de rastreamento SQL + informações de wait events + valores das bind variables.

Alguns métodos para se habilitar um trace na sua sessão:

--ativa trace nível 1
alter session set sql_trace=true;
 
--desativa trace
alter session set sql_trace=false;
 
--ativa o trace nível 8
alter session set events ‘10046 trace name context forever, level 8’;
 
--desativa o trace
alter session set events ‘10046 trace name context off’;
 
--Para quem possui o pacote DBMS_SUPPORT instalado:
 
--ativa trace nível 1
exec sys.dbms_support.start_trace;
 
--para incluir wait events e bind variables
exec sys.dbms_support.start_trace(waits => TRUE, binds=> TRUE);
 
--desativa o trace
exec sys.dbms_support.stop_trace;

Alguns métodos para se habilitar um trace em outra sessão:

--ativa o trace nível 8
execute sys.dbms_system.set_ev(SID,SERIAL#,10046,8,'');
 
--desativa o trace
execute sys.dbms_system.set_ev(SID,SERIAL#,10046,0,'');
 
--Para quem possui o pacote DBMS_SUPPORT instalado:
 
--ativa o trace nível 12
exec dbms_support.start_trace_in_session(
     sid => sid,
     serial# => serial,
     waits => true,
     binds => true);
 
--desativa o trace
exec dbms_support.stop_trace_in_session(
     sid => sid,
     serial# => serial);
 
--Utilizando o pacote DBMS_MONITOR:
 
--ativa o trace nível 12
exec dbms_monitor.session_trace_enable(
     session_id => sid,
     serial_num => serial,
     waits => true,
     binds => true);
 
--desativa o trace
exec dbms_monitor.session_trace_disable(
     session_id => sid,
     serial_num => serial);

Os arquivos de trace recebem a extensão .trc e podem ser encontrados na versão 10g em 2 diretórios dependendo da sessão em que o trace foi habilitado:

Sessão de usuário = USER_DUMP_DEST

SQL> show parameter USER_DUMP_DEST
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
user_dump_dest                       string      /oratst01/app/oracle/admin/tes
                                                 te/udump

Processos background = BACKGROUND_DUMP_DEST

SQL> show parameter BACKGROUND_DUMP_DEST
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
background_dump_dest                 string      /oratst01/app/oracle/admin/tes
                                                 te/bdump

Nas versões 11g e 12c os traces (.trc) podem ser encontrados abaixo do ADR (Automatic Diagnostic Repository) tanto para processos background como de usuários: (Diag Trace)

SQL> show parameter diagnostic_dest
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
diagnostic_dest                      string      /oratst02/app/oracle
 
SQL> col name for a20
SQL> col value for a70
SQL> select * from v$diag_info;
 
   INST_ID NAME                 VALUE
---------- -------------------- ----------------------------------------------------------------------
         1 Diag Enabled         TRUE
         1 ADR Base             /oratst02/app/oracle
         1 ADR Home             /oratst02/app/oracle/diag/rdbms/teste2/teste2
         1 Diag Trace           /oratst02/app/oracle/diag/rdbms/teste2/teste2/trace
         1 Diag Alert           /oratst02/app/oracle/diag/rdbms/teste2/teste2/alert
         1 Diag Incident        /oratst02/app/oracle/diag/rdbms/teste2/teste2/incident
         1 Diag Cdump           /oratst02/app/oracle/admin/teste2/cdump
         1 Health Monitor       /oratst02/app/oracle/diag/rdbms/teste2/teste2/hm
         1 Default Trace File   /oratst02/app/oracle/diag/rdbms/teste2/teste2/trace/teste2_ora_263.trc
         1 Active Problem Count 0
         1 Active IncidentCount 0

O nome do arquivo será: [ ORACLE_SID ]_ora_[ SPID ].trc
SQL> select spid
  2    from v$process p, v$session s
  3   where p.addr = s.paddr
  4     and sid = 2371;
 
SPID
------------
12603
..
srvtst:oracle:oratst2> ls -lrt *12603*
-rw-r--r--   1 oracle     dba           6032 Aug  8 17:02 oratst2_ora_12603.trc


Uma facilidade presente desde a versão 8.1.7 foi o TRACEFILE_IDENTIFIER que permite atribuir um “NOME” ao arquivo trace. Quando você esta realizando um trace da sua sessão:

SQL> alter session set tracefile_identifier=anderson;
 
Session altered.

agora:

srvtst:oracle:oratst2> ls -lrt *ANDERSON*
-rw-r--r--   1 oracle     dba             73 Aug  8 17:08 oratst2_ora_4796_ANDERSON.trc

Após identificar o seu arquivo trace você executa o utilitário TKPROF (Transient Kernel Profiler) para gerar e sumarizar todos os dados coletados no arquivo .trc

srvtst:oracle:oratst2> tkprof oratst2_ora_4796_ANDERSON.trc trace.txt
 
TKPROF: Release 10.2.0.5.0 - Production on Thu Aug 8 17:13:35 2013
 
Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Agora é só visualizar o arquivo analizado (trace.txt)

SELECT COUNT(*)
FROM
 TESTE WHERE TIMESTAMP < (SYSDATE  - 365)
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.03          1         30          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2     47.08     289.13    1118319    1138160          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4     47.11     289.16    1118320    1138190          0           1
 
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 20844
 
Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=1138160 pr=1118319 pw=0 time=289132678 us)
      0   TABLE ACCESS FULL TESTE (cr=1138160 pr=1118319 pw=0 time=289132666 us)
 
Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       2        0.00          0.00
  db file scattered read                      35022        0.63        249.26
  latch: object queue header operation           20        0.00          0.00
  db file sequential read                         1        0.00          0.00
  SQL*Net message from client                     2        0.00          0.00
********************************************************************************

Identificando um evento de espera a nível de sessão:

SQL> set lines 190
SQL> col EVENT for a40
SQL> col P1TEXT for a10
SQL> col P2TEXT for a10
SQL> col P3TEXT for a10
SQL> col WAIT_CLASS for a20
SQL> select sw.event,
  2         sw.state,
  3         sw.p1text,
  4         sw.p1,
  5         sw.p2text,
  6         sw.p2,
  7         sw.p3text,
  8         sw.p3,
  9         sw.wait_class
 10    from v$session_wait sw
 11   where sid = 255
 12  /
 
EVENT                                    STATE               P1TEXT             P1 P2TEXT             P2 P3TEXT             P3 WAIT_CLASS
---------------------------------------- ------------------- ---------- ---------- ---------- ---------- ---------- ---------- --------------------
db file scattered read                   WAITING             file#             448 block#          52425 blocks             16 User I/O

Conhecendo o evento(sintoma) de espera da sessão você pode agir sobre o mesmo. Neste exemplo, o evento ‘db file scattered read’ representa que a sessão esta realizando um acesso FULL sobre uma tabela – FTS (FULL TABLE SCAN) ou sobre um índice – FFS (FAST FULL SCAN).

Identificando o comando SQL da sessão 255:

SQL> SELECT A.SQL_TEXT
  2    FROM V$SQLTEXT A, V$SESSION B
  3   WHERE A.ADDRESS = B.SQL_ADDRESS
  4     AND A.HASH_VALUE = B.SQL_HASH_VALUE
  5     AND B.SID = 255
  6   ORDER BY PIECE;
 
SQL_TEXT
----------------------------------------------------------------
select * from lancamentos where cd_empresa=5

Plano de execução do SQL. Observe o TABLE ACCESS FULL na coluna Operation. (representa um acesso full sobre uma tabela)

SQL> explain plan for
  2  select * from lancamentos where cd_empresa=5;
 
Explained.
 
SQL> SELECT * FROM TABLE(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
Plan hash value: 2518875852
 
---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             |  9759K|  1237M|  1108K  (2)| 03:41:40 |
|*  1 |  TABLE ACCESS FULL| LANCAMENTOS |  9759K|  1237M|  1108K  (2)| 03:41:40 |
---------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("CD_EMPRESA"=5)
 
13 rows selected.

Agora é só agir sobre o problema! sintoma, evento de espera…

Identificando os TOP wait events do sistema: 

Se você já gerou algum report do AWR(Automatic Workload Repository) deve conhecer a imagem abaixo dos “Top 5 Timed Events”.



Este report mostra os wait events que mais afetaram o banco de dados no intervalo de snapshot selecionado, mas não é preciso gerar um AWR para coletá-los, posso pegar as informações utilizando as próprias views do AWR. Exemplo:

Selecionando o horário desejado: ( snap_id = 21138 )

SQL> select a.snap_id,
  2         to_char(a.begin_interval_time, 'dd/mm/yyyy hh24:mi:ss') begin_interval_time,
  3         to_char(a.end_interval_time, 'dd/mm/yyyy hh24:mi:ss') end_interval_time
  4    from dba_hist_snapshot a
  5   order by 1 desc;
 
   SNAP_ID BEGIN_INTERVAL_TIME END_INTERVAL_TIME
---------- ------------------- -------------------
     21145 23/07/2013 18:00:53 23/07/2013 19:00:05
     21144 23/07/2013 17:01:11 23/07/2013 18:00:53
     21143 23/07/2013 16:00:27 23/07/2013 17:01:11
     21142 23/07/2013 15:00:15 23/07/2013 16:00:27
     21141 23/07/2013 14:00:02 23/07/2013 15:00:15
     21140 23/07/2013 13:00:50 23/07/2013 14:00:02
     21139 23/07/2013 12:00:37 23/07/2013 13:00:50
     21138 23/07/2013 11:00:24 23/07/2013 12:00:37
     21137 23/07/2013 10:00:11 23/07/2013 11:00:24
     21136 23/07/2013 09:00:59 23/07/2013 10:00:11
     21135 23/07/2013 08:00:46 23/07/2013 09:00:59
     21134 23/07/2013 07:00:33 23/07/2013 08:00:46
     21133 23/07/2013 06:01:09 23/07/2013 07:00:33
     21132 23/07/2013 05:00:08 23/07/2013 06:01:09

Executando a query para coletar os TOP events:

SQL> select snap_id,
  2         begin_time,
  3         end_time,
  4         (select i.instance_name
  5            from gv$instance i
  6           where i.INSTANCE_NUMBER = a.instance_number) as "INSTANCE",
  7         event_name,
  8         total_waits as "WAITS",
  9         event_time_waited as "TIME(s)",
 10         avg_wait as "AVG_WAIT(ms)",
 11         pct as "%PCT",
 12         wait_class
 13    from (select to_char(s.begin_interval_time, 'DD-MM-YYYY HH24:MI') as BEGIN_TIME,
 14                 to_char(s.end_interval_time, 'DD-MM-YYYY HH24:MI') as END_TIME,
 15                 m.*
 16            from (select ee.instance_number,
 17                         ee.snap_id,
 18                         ee.event_name,
 19                         round(ee.event_time_waited / 1000000) event_time_waited,
 20                         ee.total_waits,
 21                         round((ee.event_time_waited * 100) /
 22                               et.total_time_waited,
 23                               1) pct,
 24                         round((ee.event_time_waited / ee.total_waits) / 1000) avg_wait,
 25                         ee.wait_class
 26                    from (select ee1.instance_number,
 27                                 ee1.snap_id,
 28                                 ee1.event_name,
 29                                 ee1.time_waited_micro - ee2.time_waited_micro event_time_waited,
 30                                 ee1.total_waits - ee2.total_waits total_waits,
 31                                 ee1.wait_class
 32                            from dba_hist_system_event ee1
 33                            join dba_hist_system_event ee2 on ee1.snap_id =
 34                                                              ee2.snap_id + 1
 35                                                          and ee1.instance_number =
 36                                                              ee2.instance_number
 37                                                          and ee1.event_id =
 38                                                              ee2.event_id
 39                                                          and ee1.wait_class_id <>
 40                                                              2723168908
 41                                                          and ee1.time_waited_micro -
 42                                                              ee2.time_waited_micro > 0
 43                          union
 44                          select st1.instance_number,
 45                                 st1.snap_id,
 46                                 st1.stat_name event_name,
 47                                 st1.value - st2.value event_time_waited,
 48                                 null total_waits,
 49                                 null wait_class
 50                            from dba_hist_sys_time_model st1
 51                            join dba_hist_sys_time_model st2 on st1.instance_number =
 52                                                                st2.instance_number
 53                                                            and st1.snap_id =
 54                                                                st2.snap_id + 1
 55                                                            and st1.stat_id =
 56                                                                st2.stat_id
 57                                                            and st1.stat_name =
 58                                                                'DB CPU'
 59                                                            and st1.value -
 60                                                                st2.value > 0) ee
 61                    join (select et1.instance_number,
 62                                et1.snap_id,
 63                                et1.value - et2.value total_time_waited,
 64                                null wait_class
 65                           from dba_hist_sys_time_model et1
 66                           join dba_hist_sys_time_model et2 on et1.snap_id =
 67                                                               et2.snap_id + 1
 68                                                           and et1.instance_number =
 69                                                               et2.instance_number
 70                                                           and et1.stat_id =
 71                                                               et2.stat_id
 72                                                           and et1.stat_name =
 73                                                               'DB time'
 74                                                           and et1.value -
 75                                                               et2.value > 0) et on ee.instance_number =
 76                                                                                    et.instance_number
 77                                                                                and ee.snap_id =
 78                                                                                    et.snap_id) m
 79            join dba_hist_snapshot s on m.snap_id = s.snap_id
 80           where m.instance_number = 1
 81             and m.snap_id = 21138 --SNAP_ID capturado na query acima
 82           order by PCT desc) a
 83   where rownum <= 5 --Quantidade de linhas retornadas, especifique 10 para ter um TOP 10
 84  /
 
   SNAP_ID BEGIN_TIME       END_TIME         INSTANCE         EVENT_NAME                            WAITS      TIME(s) AVG_WAIT(ms)       %PCT WAIT_CLASS
---------- ---------------- ---------------- ---------------- ------------------------------ ------------ ------------ ------------ ---------- --------------------
     21138 23-07-2013 11:00 23-07-2013 12:00 c0090prd         DB CPU                                            66,705                    44.6
     21138 23-07-2013 11:00 23-07-2013 12:00 c0090prd         log file sync                       868,610       15,564           18       10.4 Commit
     21138 23-07-2013 11:00 23-07-2013 12:00 c0090prd         db file sequential read           7,663,832       15,562            2       10.4 User I/O
     21138 23-07-2013 11:00 23-07-2013 12:00 c0090prd         SQL*Net message from dblink         813,766       14,805           18        9.9 Network
     21138 23-07-2013 11:00 23-07-2013 12:00 c0090prd         SQL*Net break/reset to client       174,240        7,015           40        4.7 Application

Referências:

http://docs.oracle.com/cd/B28359_01/server.111/b28320/waitevents.htm http://it.toolbox.com/blogs/living-happy-oracle/oracle-a-query-to-get-the-wait-events-part-of-the-awr-report-33420 http://vapvarun.com/study/oracle/mcgraw.hill.osborne.oracle.wait.interface.a.practical.guide.to.performance.d
Mais informações →

sexta-feira, 12 de abril de 2013

Particionando uma tabela existente utilizando DBMS_REDEFINITION

O Particionamento é uma solução oferecida na versão Enterprise Edition(EE) – With Partitioning, mediante licenciamento, que permite particionarmos tabelas e índices em pedaços menores simplificando sua administração e melhorando o desempenho de consultas. 

A Oracle oferece alguns métodos de particionamento, como podemos verificar abaixo: 

Range Partitioning: Particionamento por intervalos. É o tipo mais comum de particionamento e normalmente utilizado sobre datas; 

List Partitioning: Particionamento em listas, onde podemos especificar uma lista de valores atribuidos à cada partição; 

Hash Partitioning: Particionamento em faixas, normalmente dados que não encaixam no formato de particionamento de listas ou ranges; 

Composite Partitioning: Particionamento composto por partições range e sub-partições (list ou hash). 

Neste artigo irei demonstrar como podemos particionar uma tabela já existente utilizando o modelo range(intervalo) através da package DBMS_REDEFINITION existente desde a versão 9i e que permite uma redefinição ONLINE de tabelas. 

Primeiramente vamos criar e popular duas simples tabelas contendo constraints e índices.

SQL> create table forma_pagamento (
  2  codigo number(2) constraint pk_codigo primary key,
  3  descricao varchar2(50)
  4  );
 
Table created.
 
insert into forma_pagamento values (1,'A VISTA');
insert into forma_pagamento values (2,'BOLETO BANCARIO');
insert into forma_pagamento values (3,'TRANSFERENCIA BANCARIA');
commit;
 
SQL> select * from forma_pagamento;
 
    CODIGO DESCRICAO
---------- --------------------------------------------------
         1 A VISTA
         2 BOLETO BANCARIO
         3 TRANSFERENCIA BANCARIA
 
SQL> create table venda (
  2  id number(7) constraint pk_id primary key,
  3  codigo_pag number(2),
  4  data_venda date,
  5  constraint fk_venda_forma_pag foreign key (codigo_pag) references forma_pagamento(codigo)
  6  );
 
Table created.
 
SQL> create index venda_idx1 on venda(codigo_pag);
 
Index created.
 
SQL> create index venda_idx2 on venda(codigo_pag,data_venda);
 
Index created.
 
SQL> DECLARE
  2    wk_codigo_pag    forma_pagamento.codigo%TYPE;
  3    wk_data_venda  DATE;
  4  BEGIN
  5    FOR i IN 1 .. 1000000 LOOP
  6
  7      SELECT ROUND(DBMS_RANDOM.VALUE(1,3)) INTO wk_codigo_pag FROM DUAL;
  8
  9      IF MOD(i, 3) = 0 THEN
 10        wk_data_venda := ADD_MONTHS(SYSDATE, -24);
 11      ELSIF MOD(i, 2) = 0 THEN
 12        wk_data_venda := ADD_MONTHS(SYSDATE, -12);
 13      ELSE
 14        wk_data_venda := SYSDATE;
 15      END IF;
 16
 17      INSERT INTO VENDA (id, codigo_pag, data_venda)
 18      VALUES (i, wk_codigo_pag, wk_data_venda);
 19
 20    END LOOP;
 21    COMMIT;
 22  END;
 23  /
 
PL/SQL procedure successfully completed.
 
SQL> commit;
 
Commit complete.

Criado e populado as tabelas vamos verificar as constraints e índices da tabela VENDA que será particionada posteriormente.

SQL> select constraint_name,constraint_type
  2  from user_constraints
  3  where table_name='VENDA';
 
CONSTRAINT_NAME                C
------------------------------ -
PK_ID                          P
FK_VENDA_FORMA_PAG             R
 
SQL> select index_name
  2  from dba_indexes
  3  where table_name='VENDA';
 
INDEX_NAME
------------------------------
VENDA_IDX2
VENDA_IDX1
PK_ID

Montado nosso cenário, vamos iniciar o processo de particionamento ONLINE da tabela VENDA (DATA_VENDA) por range(intervalo) utilizando a DBMS_REDEFINITION. 

Nosso primeiro passo é verificar se a tabela pode ser redefinida de forma online. Caso não seja uma candidata, a procedure ira retornar um erro indicando o motivo.

SQL> BEGIN
  2  DBMS_REDEFINITION.CAN_REDEF_TABLE(uname => 'ANDERSON', tname => 'VENDA');
  3  END;
  4  /
 
PL/SQL procedure successfully completed.

Neste exemplo não estou especificando a opção OPTIONS_FLAG, desta forma a procedure CAN_REDEF_TABLE utiliza a opção default DBMS_REDEFINITION.CONS_USE_PK que realiza a redefinição usando a chave-primária da tabela (condição existente na nossa tabela). Caso a tabela não possua PK, podemos especificar OPTIONS_FLAG=>DBMS_REDEFINITION.CONS_USE_ROWID 

Agora iremos criar nossa tabela temporária, conhecida por INTERIM, utilizando a mesma estrutura da tabela anterior (VENDA) porem agora particionada e sem constraints/índices.

SQL> create table venda_interim (
  2  id number(7),
  3  codigo_pag number(2),
  4  data_venda date
  5  )
  6  partition by range (DATA_VENDA)
  7  (
  8     partition PRANGE2011 values less than (TO_DATE(' 2012-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
  9       tablespace USERS
 10       ,
 11     partition PRANGE2012 values less than (TO_DATE(' 2013-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
 12       tablespace USERS
 13       ,
 14     partition PRANGE2013 values less than (TO_DATE(' 2014-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
 16       tablespace USERS
 15  )
 17  tablespace USERS;
 
Table created.

O próximo passo é iniciar o processo de redefinição online da tabela, vejamos:

SQL> BEGIN
  2  DBMS_REDEFINITION.start_redef_table(
  3    uname      => 'ANDERSON',
  4    orig_table => 'VENDA',
  5    int_table  => 'VENDA_INTERIM');
  6  END;
  7  /
 
PL/SQL procedure successfully completed.

Dando sequencia iremos realizar a cópia dos objetos dependentes para a tabela INTERIM, como grants, triggers, constraints, índices e privilégios.

SQL> var num_errors number
SQL> BEGIN
  2    DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS(uname      => 'ANDERSON',
  3                                            orig_table => 'VENDA',
  4                                            int_table  => 'VENDA_INTERIM',
  5                                            num_errors => :num_errors);
  6  END;
  7  /
 
PL/SQL procedure successfully completed.
 
SQL> print num_errors
 
NUM_ERRORS
----------
         0

Vamos disparar um sincronismo para manter a tabela INTERIM sincronizada com a tabela original(VENDA). No nosso cenário não temos nenhum benefício, pois ninguém esta alterando ou inserindo registros na tabela VENDA, contudo em um ambiente OLTP cuja tabela esta sofrendo constantes operações DML o sincronismo é útil para minimizar a quantidade de sincronização necessária a ser feita pelo procedimento FINISH_REDEF_TABLE(abaixo) acelerando as operações subsequentes.

SQL> BEGIN
  2  DBMS_REDEFINITION.sync_interim_table(
  3    uname      => 'ANDERSON',
  4    orig_table => 'VENDA',
  5    int_table  => 'VENDA_INTERIM');
  6  END;
  7  /
 
PL/SQL procedure successfully completed.

Por fim, disparamos uma coleta de estatística sobre a VENDA_INTERIM e finalizamos o processo de redefinição.

SQL> BEGIN
  2  DBMS_STATS.GATHER_TABLE_STATS(ownname          => 'ANDERSON',
  3                                TABNAME          => 'VENDA_INTERIM',
  4                                estimate_percent => 100,
  5                                method_opt       => 'FOR ALL COLUMNS SIZE 1',
  6                                degree           => 16,
  7                                granularity      => 'ALL',
  8                                cascade          => TRUE);
  9  END;
 10  /
 
PL/SQL procedure successfully completed.
 
SQL> BEGIN
  2  DBMS_REDEFINITION.finish_redef_table(
  3    uname      => 'ANDERSON',
  4    orig_table => 'VENDA',
  5    int_table  => 'VENDA_INTERIM');
  6  END;
  7 /
 
PL/SQL procedure successfully completed.

Agora já podemos remover a tabela INTERIM(VENDA_INTERIM)

SQL> drop table venda_interim;
 
Table dropped.

Vamos verificar nossas constrains, índices e os particionamentos da tabela.

SQL> select constraint_name,constraint_type, status
  2  from user_constraints
  3  where table_name='VENDA';
 
CONSTRAINT_NAME                C STATUS
------------------------------ - --------
PK_ID                          P ENABLED
FK_VENDA_FORMA_PAG             R ENABLED
 
SQL> select index_name,status
  2  from user_indexes
  3  where table_name='VENDA';
 
INDEX_NAME                     STATUS
------------------------------ --------
PK_ID                          VALID
VENDA_IDX1                     VALID
VENDA_IDX2                     VALID
 
SQL> select table_name, partition_name, num_rows
  2  from dba_tab_partitions
  3  where table_name='VENDA';
 
TABLE_NAME                     PARTITION_NAME                   NUM_ROWS
------------------------------ ------------------------------ ----------
VENDA                          PRANGE2011                         333333
VENDA                          PRANGE2012                         333334
VENDA                          PRANGE2013                         333333

Em um próximo artigo será abordado métodos de particionamento e também particionamento de índices(LOCAL, GLOBAL..), fique ligado!

FONTES:
http://docs.oracle.com/cd/B19306_01/server.102/b14220/partconc.htm#i460895 http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_redefi.htm
Mais informações →

quinta-feira, 14 de março de 2013

Restaurando estatísticas antigas via dbms_stats.restore_table_stats

Em um artigo passado, Uso e gerenciamento dos ocupantes da SYXAUX, havíamos falado sobre o SM/OPTSTAT cuja feature introduzida na versão 10G armazena versões mais velhas do otimizador de estatísticas o que nos permite restaurá-las caso seja encontrado alguma agressão com o novo plano de execução em função de uma nova coleta de estatística, por exemplo. 

Pois bem, hoje iremos demonstrar como podemos restaurar estas estatísticas antigas armazenadas na SM/OPTSTAT. 

Pegando uma tabela qualquer para testes, vamos verificar quando ocorreu a ultima coleta de estatisticas e a quantidade de linhas da tabela.

SQL> set lines 190
SQL> select table_name, to_char(last_analyzed,'dd/mm/yyyy hh24:mi') last_analyzed , num_rows from dba_tables where table_name='TESTE_OLD_STATS';
 
TABLE_NAME                     LAST_ANALYZED      NUM_ROWS
------------------------------ ---------------- ----------
TESTE_OLD_STATS                12/03/2013 22:15     457678

Observem que a ultima coleta ocorreu no dia 12/03 as 23:30 e a tabela possuia 457.678 registros. Como o plano de execução é baseado em estatisticas, vejamos a quantidade de linhas(rows) retornadas caso um acesso full a tabela fosse realizado (FTS) – FULL TABLE SCAN

SQL> set lines 190
SQL> set pages 6000
SQL> explain plan for
  2  select * from TESTE_OLD_STATS
  3  /
 
Explained.
 
SQL> SELECT * FROM TABLE(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
Plan hash value: 207430563
 
-------------------------------------------------------------------------------------
| Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                 |   457K|    28M|   956   (2)| 00:00:12 |
|   1 |  TABLE ACCESS FULL| TESTE_OLD_STATS |   457K|    28M|   956   (2)| 00:00:12 |
-------------------------------------------------------------------------------------
 
8 rows selected.

Vamos verificar a quantidade de dias que as estatísticas estão sendo retidas. Por default são 31 dias, mas isto pode ser ajustado conforme necessidade, mais ou menos (verificar artigo: Uso e gerenciamento dos ocupantes da SYXAUX)

SQL> select dbms_stats.get_stats_history_retention from dual;
 
GET_STATS_HISTORY_RETENTION
---------------------------
                         31

ou

SQL> select dbms_stats.get_stats_history_availability from dual;
 
GET_STATS_HISTORY_AVAILABILITY
---------------------------------------------------------------------------
09-FEB-13 10.29.55.270730000 PM -03:00

Agora as estatísticas antigas registradas para nossa tabela dentro do período de retenção (31 dias)

SQL> select a.table_name, a.stats_update_time from DBA_TAB_STATS_HISTORY a where a.table_name='TESTE_OLD_STATS';
 
TABLE_NAME                     STATS_UPDATE_TIME
------------------------------ ---------------------------------------------------------------------------
TESTE_OLD_STATS                10-FEB-13 12.39.05.828592 AM -02:00
TESTE_OLD_STATS                17-FEB-13 01.58.31.076713 AM -03:00
TESTE_OLD_STATS                17-FEB-13 10.05.56.367845 PM -03:00
TESTE_OLD_STATS                19-FEB-13 10.16.20.671026 PM -03:00
TESTE_OLD_STATS                20-FEB-13 02.14.35.927244 PM -03:00
TESTE_OLD_STATS                20-FEB-13 10.16.38.776073 PM -03:00
TESTE_OLD_STATS                21-FEB-13 10.19.12.034280 PM -03:00
TESTE_OLD_STATS                22-FEB-13 10.18.28.355258 PM -03:00
TESTE_OLD_STATS                27-FEB-13 10.20.22.307535 PM -03:00
TESTE_OLD_STATS                28-FEB-13 10.16.40.663599 PM -03:00
TESTE_OLD_STATS                12-MAR-13 10.15.52.573286 PM -03:00
 
11 rows selected.

Restaurando as estatísticas do dia 19/02 (19-FEB-13)

SQL> exec dbms_stats.restore_table_stats('ANDERSON', 'TESTE_OLD_STATS','19-FEB-13 10.16.20.671026 PM -03:00');
 
PL/SQL procedure successfully completed.
 
SQL> select table_name, to_char(last_analyzed,'dd/mm/yyyy hh24:mi') last_analyzed , num_rows from dba_tables where table_name='TESTE_OLD_STATS';
 
TABLE_NAME                     LAST_ANALYZED      NUM_ROWS
------------------------------ ---------------- ----------
TESTE_OLD_STATS                19/02/2013 22:16     256982

Novamente um explain com FTS. Observe a quantidade de linhas (rows)

SQL> explain plan for
  2  select * from TESTE_OLD_STATS
  3  /
 
Explained.
 
SQL> SELECT * FROM TABLE(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
Plan hash value: 207430563
 
-------------------------------------------------------------------------------------
| Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                 |   256K|    15M|   227   (4)| 00:00:03 |
|   1 |  TABLE ACCESS FULL| TESTE_OLD_STATS |   256K|    15M|   227   (4)| 00:00:03 |
-------------------------------------------------------------------------------------
 
8 rows selected.

Outros detalhes sobre a coleta de estatísticas da tabela também podem ser obtidos na DBA_OPTSTAT_OPERATIONS, conforme abaixo:

SQL> set lines 190
SQL> col OPERATION for a25
SQL> col TARGET for a40
SQL> col START_TIME for a40
SQL> col END_TIME for a40
SQL> select a.* from DBA_OPTSTAT_OPERATIONS  a where target like '%TESTE_OLD_STATS%';
 
OPERATION                                TARGET                                   START_TIME                               END_TIME
---------------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
restore_table_stats                      ANDERSON.TESTE_OLD_STATS                 13-MAR-13 08.45.18.660761 PM -03:00      13-MAR-13 08.45.19.015789 PM -03:00
gather_table_stats                       ANDERSON."TESTE_OLD_STATS"               10-FEB-13 12.39.05.190460 AM -02:00      10-FEB-13 12.39.07.042604 AM -02:00
gather_table_stats                       ANDERSON."TESTE_OLD_STATS"               17-FEB-13 01.58.30.549778 AM -03:00      17-FEB-13 01.58.32.001111 AM -03:00

Só para concluir, lembro que este não é o único modo de restaurar as estatísticas de uma tabela, você pode exportar/importar/replicar utilizando um backup, por exemplo.
Mais informações →

sexta-feira, 16 de março de 2012

Descobrindo o DBID sem acesso ao database OPEN

Algo que muitos DBA’s não dão importância é de registrar o DBID do banco de dados em um local seguro e de fácil acesso para que seja utilizado quando for preciso!

E agora que perdemos todo nosso ambiente ou parte dele e não temos o DBID anotado, como podemos descobri-lo?

Possuímos duas formas, uma através dos arquivos de backup (backupset ou copia-imagem) outra através dos próprios datafiles. Os comandos variam conforme o arquivo utilizado, SYSAUX, SYSTEM/UNDO ou Backupset através do comando strings do Linux.

Para identificarmos o DBID através do datafile da tablespace SYSAUX, por exemplo, utilizamos a seguinte sintaxe:

[oracle@orcl ~]$ strings /u02/oradata/orcl10g/sysaux01.dbf|grep DB_ID
 
DB_ID   941982635

Onde nosso DBID é : 941982635

Já através da utilização dos datafiles da SYSTEM/UNDO ou Backupsets devemos usar a seguinte sintaxe:

[oracle@orcl ~]$ strings /u02/oradata/orcl10g/system01.dbf|grep MAXVALUE,
 
941982635, MAXVALUE,
[oracle@orcl ~]$ strings /orabackup/orcl10g/backupset/df_ORCL10G_11_1_777835981.dbf|grep MAXVALUE,
 
941982635, MAXVALUE,

Pronto, já identificamos nosso DBID e podemos recuperar nosso banco de dados. Lembre-se agora de anotar o BBID do banco!

Para capturar o DBID quando o database está OPEN basta uma consulta na v$database conforme demonstrado abaixo:

SQL> SELECT DBID FROM V$DATABASE;
 
      DBID
----------
 941982635

Outra forma já monstrada aqui no ORACLE HOME é registrar o DBID no alert log do Oracle, Escrevendo no Alert log
Mais informações →

domingo, 19 de fevereiro de 2012

Escrevendo no Alert log

O arquivo de log de alerta do Oracle conhecido basicamente por alert.log é um log de mensagens e erros escritos pelo banco de dados que registra informações como desligamento e inicialização do banco, alterações de parâmetros, erros/alertas, entre outros. 

Como sabemos este arquivo deve ser constantemente monitorado para detectar mensagens de alertas e erros, mas como podemos testar se nosso monitoramento esta realmente funcionando sem gerar um erro ou esperar que um aconteça? 

Pois bem, a Oracle oferece uma package para podermos “escrever” no alertlog e arquivos de trace, vejamos: 

O primeiro valor informado na chamada tem como objetivo:

1 – Gravar o erro em um arquivo de trace (trace file);
2 – Gravar o erro no alertlog;
3 – Gravar o erro no alertlog e em um arquivo trace.

O segundo é a mensagem ou texto a ser escrito:

SQL> EXEC SYS.DBMS_SYSTEM.KSDWRT(2,'ORA-00600: TESTE ANDERSON');
 
PL/SQL procedure successfully completed.
 
SQL> EXEC SYS.DBMS_SYSTEM.KSDWRT(2,'ORA-00600: internal error code, arguments: [ANDERSON], [12345], [], [], [], [], []');
 
PL/SQL procedure successfully completed.

Verificando o alert log podemos constatar que a simulação foi realizada com sucesso.

maquina-de-teste > tail -n 10 alert_teste.log
  Current log# 2 seq# 17929 mem# 0: /oraprd01/app/oracle/oradata/teste/redo02a.log
  Current log# 2 seq# 17929 mem# 1: /oraprd02/oradata/teste/redo02b.log
Mon Feb 06 18:08:50 2012
Archived Log entry 26302 added for thread 1 sequence 17928 ID 0x341cf6e dest 1:
Archived Log entry 26303 added for thread 1 sequence 17928 ID 0x341cf6e dest 2:
Mon Feb 06 18:12:19 2012
Mon Feb 06 18:13:36 2012
ORA-00600: TESTE ANDERSON
Mon Feb 06 18:14:34 2012
ORA-00600: internal error code, arguments: [ANDERSON], [12345], [], [], [], [], []

Uma boa prática que podemos adotar é utilizar esta package para registrar o DBID do database no alertlog(via trigger, scheduler, etc..), visto que é fundamental também matermos registrado em outros locais assim como uma copia dos backups pois nunca sabemos quando um desastre pode ocorrer…

SQL> declare
  dbid number;
begin
  select dbid into dbid from v$database;
  dbms_system.ksdwrt(2,'DBID = '||dbid);
end; 
/

PL/SQL procedure successfully completed.

Alert log do banco de dados:

maquina-de-teste > tail -n 10 alert_teste.log
Mon Feb 06 18:08:50 2012
Archived Log entry 26302 added for thread 1 sequence 17928 ID 0x341cf6e dest 1:
Archived Log entry 26303 added for thread 1 sequence 17928 ID 0x341cf6e dest 2:
Mon Feb 06 18:12:19 2012
Mon Feb 06 18:13:36 2012
ORA-00600: TESTE ANDERSON
Mon Feb 06 18:14:34 2012
ORA-00600: internal error code, arguments: [ANDERSON], [12345], [], [], [], [], []
Mon Feb 06 18:22:50 2012
DBID = 54669934
Mais informações →
Postagens mais antigas Página inicial

Translate

# Suporte

# ACE Program

#Oracle

#Oracle
Disclaimer: The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.

#Blog reconhecido

#ARTICULISTA

Marcadores

Postagens populares