Introduzione

Lo STANDBY database è un’ottima soluzione sia per un ripristino rapido dei servizi in caso di disastro sul PRIMARIO e sia come database da cui effettuare i backup senza impatti sul database di produzione. Ma come risolvere se un problema di corruzione o perdita datafile si verifica proprio sullo STANDBY?

Ovviamente la soluzione standard è quella di utilizzare il backup RMAN disponibile, ma non sempre può essere la scelta giusta. Infatti nel caso in cui i backup fossero su nastro il ripristino potrebbe risultare estremamente lento.

Se invece tra lo STANDBY e il PRIMARIO la connessione di rete è abbastanza veloce, si può pensare di utilizzare quest’ultimo per il ripristino sfruttando la funzionalità “RESTORE FROM SERVICE” disponibile nelle più recenti versioni del database oracle. Approfondiamo con un esempio.

Ambiente

  1. Oracle Enterprise Edition
  2. Database PRIMARIO oracle RAC
  3. STANDBY Database oracle single instance
  4. Database oracle versione 12.1.0.2
  5. Pluggable database
  6. Storage gestito da ASM
  7. Cluster GRID oracle versione 12.1.0.2
  8. STANDBY gestito con Data Guard Broker
  9. Sistema Operativo Oracle Linux 7.5

Scenario

  1. Creazione ambiente per il test
  2. Validazione del datafile
  3. Corruzione del datafile
  4. Ripristino dal PRIMARIO
  5. Test finale di verifica

Database coinvolti

  • LABLAR – Database Primario RAC
  • LABLBR – Standby database Single Instance
  • LABPDBL – Pluggable database

Configurazione tnsnames.ora

LAB =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = Host_A)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = Host_B)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = LABPDBL_SRV)
    )
  )

Creazione ambiente per il test

Creare nel pluggable database un tablespace dedicato, una utenza dedicata e una tabella per effettuare il test.

--PRIMARY 
 
$ sqlplus / as sysdba
 
-- Connessione al pluggable database
 
SQL> alter session set container=LABPDBL;
 
-- Creazione tablespace
 
SQL> create tablespace appo_data datafile '+DG_DATA' size 1G;
 
SQL> create user appo_user identified by appo2020;
 
SQL> grant connect, create table to appo_user;
 
SQL> alter user appo_user quota unlimited on appo_data;
 
-- Creazione tabella
 
SQL> CREATE TABLE appo_user.appo_tab (
  id          NUMBER,
  description VARCHAR2(50),
  CONSTRAINT tab1_pk PRIMARY KEY (id)
) tablespace appo_data 
;
 
-- Popolamento tabella
 
SQL> INSERT INTO appo_user.appo_tab SELECT level, 'Description of ' || level
FROM   dual CONNECT BY level <= 100000;
 
SQL> COMMIT;
 
-- Verifica contenuto tabella
 
SELECT COUNT(*) FROM appo_user.appo_tab;
 
  COUNT(*)
----------
    100000

Verifica dello stato dei database PRIMARIO  e STANDBY:

Verifichiamo che entrambi i database PRIMARIO  e STANDBY siano nella configurazione corretta per questo test (lo STANDBY database sarà in stato di mount).

-- PRIMARY 
 
SQL> select NAME, DB_UNIQUE_NAME, OPEN_MODE, DATABASE_ROLE from v$database;
 
NAME      DB_UNIQUE_NAME                 OPEN_MODE            DATABASE_ROLE
--------- ------------------------------ -------------------- ----------------
LABL      LABLAR                         READ WRITE           PRIMARY
 
-- STANDBY
 
SQL> select NAME, DB_UNIQUE_NAME, OPEN_MODE, DATABASE_ROLE from v$database;
 
NAME      DB_UNIQUE_NAME                 OPEN_MODE            DATABASE_ROLE
--------- ------------------------------ -------------------- ----------------
LABL      LABLBR                         MOUNTED              PHYSICAL STANDBY

Verifica della lista dei datafile:

La verifica dovrà essere fatta utilizzando la vista V$DATAFILE in quanto sul database di STANDBY, essendo in MOUNT, la vista CDB_DATA_FILES non è disponibile (vista alternativa alla DBA_DATA_FILES per poter verificare tutti i datafiles del container compresi i pluggable database).

Verifichiamo con un esempio:

-- STANDBY:
 
$ sqlplus / as sysdba
 
SQL>select FILE_NAME from cdb_data_files;
select FILE_NAME from dba_data_files
                      *
ERROR at line 1:
ORA-01219: database or pluggable database not open: queries allowed on fixed tables or views only
 
SQL> select file#, name, status from v$datafile;
 
FILE# NAME                                                                                STATUS
----- ----------------------------------------------------------------------------------- -------
    1 +DG_DATA/LABLBR/DATAFILE/system.277.1054479973                                      SYSTEM
   10 +DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/users.264.1054480307      ONLINE
   14 +DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1055083539  ONLINE

Validazione stato del datafile da corrompere sullo STANDBY:

Verifichiamo lo stato del datafile che andremo a corrompere per il test. Affinchè ci sia connessione tra il PRIMARIO e lo STANDBY è sempre buona norma utilizzare una connessione esplicita per evitare errori come ad esempio:

-- STANDBY:
 
$ rman target / nocatalog
 
RMAN> backup database;
 
Starting backup at 26.10.2020 18:32:40
RMAN-06820: WARNING: failed to archive current log at primary database
ORACLE error from target database:
ORA-17629: Cannot connect to the remote database server
ORA-17627: ORA-00942: table or view does not exist

Quindi utilizzare la connessione come segue:

$ rman target sys@lablbr nocatalog
 
RMAN> validate datafile 14;
 
...
List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
14   OK     0              477          1280            29044407
  File Name: +DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1054837221
  Block Type Blocks Failing Blocks Processed
  ---------- -------------- ----------------
  Data       0              434
  Index      0              214
  Other      0              155

Come si può vedere il datafile non presenta problemi e non ci sono blocchi corrotti.

Corruzione del datafile di STANDBY

Prima di procedere alla corruzione del datafile fermiamo la sincronizzazione tra PRIMARIO  e STANDBY utilizzando il data guard broker.

Questo si rende necessario in quanto per corrompere il datafile dovremo rimuoverlo dal database e poi ripristinarlo corrotto.

Se non interrompessimo la sincronizzazione non potremmo portare a buone fine questa operazione perchè il PRIMARIO  potrebbe cercare di scrivere sul file che stiamo corrompendo impedendoci di completare l’operazione.

$ dgmgrl
DGMGRL for Linux: Version 12.1.0.2.0 - 64bit Production
 
Copyright (c) 2000, 2013, Oracle. All rights reserved.
 
Welcome to DGMGRL, type "help" for information.
DGMGRL> connect sys@lablbr
Password:
Connected as SYSDBA.
 
DGMGRL> edit database LABLBR set state='apply-off';

Effettuiamo un backup locale del datafile che vogliamo corrompere (salviamo il TAG per utilizzarlo in seguito):

-- STANDBY
 
$ rman target sys@lablbr nocatalog
 
RMAN> backup as copy datafile 14 format '/tmp/appo_data_%f';
 
Starting backup at 26.10.2020 19:10:30
using channel ORA_DISK_1
using channel ORA_DISK_2
channel ORA_DISK_1: starting datafile copy
input datafile file number=00014 name=+DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1054837221
output file name=/tmp/appo_data_14 tag=TAG20201026T191030 RECID=26 STAMP=1054840231
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:01

Corrompiamo la copia di backup appena fatta:

-- STANDBY:
 
$ dd of=/tmp/appo_data_14 bs=8192 conv=notrunc seek=11 << EOF
Sezione corrotta!
EOF


0+1 records in
0+1 records out
 
$ dd of=/tmp/appo_data_14 bs=8192 conv=notrunc seek=12 << EOF
Sezione corrotta!
EOF


0+1 records in
0+1 records out

Verifichiamo lo stato della copia di backup usando questa volta l’utility dbv:

-- STANDBY:
 
$ dbv file=/tmp/appo_data_14 logfile=/tmp/dbv_appo_data_14.log blocksize=8192 userid=sys
 
$ more dbv_appo_data_14.log
 
DBVERIFY: Release 12.1.0.2.0 - Production on Mon Oct 26 19:13:47 2020
 
Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.
 
 
DBVERIFY - Verification starting : FILE = /tmp/appo_data_14
Page 11 is marked corrupt
Corrupt block relative dba: 0x0380000b (file 14, block 11)
...
 
Page 12 is marked corrupt
Corrupt block relative dba: 0x0380000c (file 14, block 12)
...

A questo punto facciamo un restore della copia corrotta nel database di STANDBY marcando il datafile come corrotto:

-- STANDBY:
 
RMAN> RUN
{
SET MAXCORRUPT FOR DATAFILE 14 TO 2;
RESTORE DATAFILE 14 FROM TAG 'TAG20201026T191030';
}

Verifica corruzione del datafile di STANDBY

Verificando nuovamente lo stato del datafile potremo osservare che risulterà corrotto:

RMAN> validate datafile 14;
 
Starting validate at 26.10.2020 19:17:55
...
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
14   OK     2              477          1280            29044407
 
File Name: +DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1054837221
...

Possiamo anche verificare la lista degli errori usando il comando “LIST FAILURE” di RMAN, sicuramente molto utile:

RMAN> list failure;
 
Database Role: PHYSICAL STANDBY
 
List of Database Failures
=========================
 
Failure ID Priority Status    Time Detected       Summary
---------- -------- --------- ------------------- -------
34305      HIGH     OPEN      26.10.2020 19:17:56 Datafile 14: '+DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1054837221' contains one or more corrupt blocks
...

Oppure avere suggerimenti di risoluzione usando il comando “ADVISE FAILURE” che se possibile genererà anche degli script per risolvere il problema:

RMAN> advise failure;
 
Database Role: PHYSICAL STANDBY
 
List of Database Failures
=========================
 
Failure ID Priority Status    Time Detected       Summary
---------- -------- --------- ------------------- -------
34305      HIGH     OPEN      26.10.2020 19:17:56 Datafile 14: '+DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1054837221' contains one or more corrupt blocks
...

ATTENZIONE!

Lo script generato da ADVISE FAILURE potrebbe contenere una fase di RECOVER che non va assolutamente utilizzata in questo contesto in quanto automatica al riavvio della sincronizzazione col PRIMARIO .

Ripristino sincronizzazione tra i nodi

Andiamo adesso a ripristinare la sincronizzazione con lo STANDBY in modo da simulare una situazione reale:

DGMGRL> edit database LABLBR set state='apply-on';

Come si può vedere il datafile corrotto genera un errore:

DGMGRL> show configuration;
 
Configuration - dgconfig_labl
 
  Protection Mode: MaxPerformance
  Members:
  lablar - Primary database
    lablbr - Physical STANDBY database
      Error: ORA-16766: Redo Apply is stopped
 
Fast-Start Failover: DISABLED
 
Configuration Status:
ERROR   (status updated 33 seconds ago)

Ripristino datafile fallito

Per poter effettuare il ripristino per prima cosa dobbiamo nuovamente interrompere la sincronizzazione:

$ dgmgrl
 
DGMGRL for Linux: Version 12.1.0.2.0 - 64bit Production
 
Copyright (c) 2000, 2013, Oracle. All rights reserved.
 
Welcome to DGMGRL, type "help" for information.
DGMGRL> connect sys@lablbr
Password:
Connected as SYSDBA.
 
DGMGRL> edit database LABLBR set state='apply-off';

A questo punto possiamo procedere con il ripristino recuperando il datafile dal PRIMARIO:

$ rman target sys@lablbr nocatalog
 
RMAN> restore datafile 14 from service LABLAR;
 
Starting restore at 29.10.2020 14:45:38
using channel ORA_DISK_1
using channel ORA_DISK_2
 
channel ORA_DISK_1: starting datafile backup set restore
channel ORA_DISK_1: using network backup set from service LABLAR
channel ORA_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_DISK_1: restoring datafile 00014 to +DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1054919667
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
Finished restore at 29.10.2020 14:45:40

Verifica andamento e risultato del ripristino

Possiamo verificare l’andamento del ripristino facendo un controllo sulla percentuale:

SQL>  TTITLE LEFT '% Completed. Aggregate is the overall progress:'
SET LINE 132
COL "Operation Name" FOR A50
 
SELECT opname "Operation Name", round(sofar/totalwork*100) "% Complete"
  FROM gv$session_longops
 WHERE opname LIKE 'RMAN%'
   AND totalwork != 0
   AND sofar <> totalwork
 ORDER BY 1;
 
% Completed. Aggregate is the overall progress:
OPNAME                                                          % Complete
--------------------------------------------------------------- ----------
RMAN: aggregate input                                                   80
RMAN: full datafile restore                                             75

sulla velocità di trasferimento:

SQL>  TTITLE OFF
SET HEAD OFF
 
SELECT 'Throughput: '||
       ROUND(SUM(v.value/1024/1024),1) || ' Meg so far @ ' ||
       ROUND(SUM(v.value     /1024/1024)/NVL((SELECT MIN(elapsed_seconds)
            FROM v$session_longops
            WHERE opname          LIKE 'RMAN: aggregate input'
              AND sofar           != TOTALWORK
              AND elapsed_seconds IS NOT NULL
       ),SUM(v.value     /1024/1024)),2) || ' Meg/sec'
 FROM gv$sesstat v, v$statname n, gv$session s
WHERE v.statistic# = n.statistic#
  AND n.name       = 'physical write total bytes'
  AND v.sid        = s.sid
  AND v.inst_id    = s.inst_id
  AND s.program LIKE 'rman@%'
GROUP BY n.name
/
 
Files currently being written to
FILENAME                                                  BYTES   IO_COUNT
---------------------------------------------------- ---------- ----------
/dev/data/orcl_system_d1                             6251741184      47698
/dev/data/orcl_users_d7                              6251610112      47697
 
Throughput: 94414.4 Meg so far @ 7.48 Meg/sec

Con queste informazioni andiamo a  calcolare quanto tempo il sistema impiegherà per completare l’attività:

Datafile = 32G
Throughput = 7.48 Meg/Sec
Total minutes = 32Gbytes*1024 = 32.768 Mbytes/7,48 Meg/Sec = 4.380 sec./60 = 73 Min.

Una volta completato il restore non sarà necessario effettuare una operazione di “RECOVER”, in quanto essendo uno STANDBY database (che normalmente è in stato di MOUNT) questa avverrà automaticamente attivando nuovamente la sincronizzazione:

$ dgmgrl
 
DGMGRL for Linux: Version 12.1.0.2.0 - 64bit Production
 
Copyright (c) 2000, 2013, Oracle. All rights reserved.
 
Welcome to DGMGRL, type "help" for information.
DGMGRL> connect sys@lablbr
Password:
Connected as SYSDBA.
 
DGMGRL> edit database LABLBR set state='apply-on';
Succeeded.
DGMGRL> show database verbose lablbr;
 
Database - lablbr
 
  Role:               PHYSICAL STANDBY
  Intended State:     APPLY-ON
  Transport Lag:      0 seconds (computed 0 seconds ago)
  Apply Lag:          1 hour(s) 36 seconds (computed 0 seconds ago)
...

Attendiamo quindi che la sincronizzazione sia completata:

DGMGRL> show database verbose lablbr;
 
Database - lablbr
 
  Role:               PHYSICAL STANDBY
  Intended State:     APPLY-ON
  Transport Lag:      0 seconds (computed 1 second ago)
  Apply Lag:          0 seconds (computed 1 second ago)
...

Ripetiamo quindi la verifica sul datafile corrotto:

RMAN> validate datafile 14;
 
Starting validate at 29.10.2020 14:49:13
using channel ORA_DISK_1
using channel ORA_DISK_2
channel ORA_DISK_1: starting validation of datafile
channel ORA_DISK_1: specifying datafile(s) for validation
input datafile file number=00014 name=+DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1055083539
channel ORA_DISK_1: validation complete, elapsed time: 00:00:01
List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
14   OK                 93           1281            29163410
  File Name: +DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo_data.282.1055083539

Che risulterà appunto ripristinato.

Inoltre facendo un check sulla vista V$DATAFILE osserviamo che il datafile è correttamente ONLINE:

SQL:LABLBR:>select file#, name, STATUS from v$datafile;
 
     FILE# NAME                                                                             STATUS
---------- -------------------------------------------------------------------------------- -------
...
        12 +DG_DATA/LABLBR/934D9678C27F6A69E053C90C140A1E1C/DATAFILE/appo.267.1054488329    ONLINE

Anche RMAN non presenterà alcun errore nella lista dei “fallimenti”:

RMAN> list failure;
 
Database Role: PHYSICAL STANDBY
 
no failures found that match specification

Test finale

Facciamo per completezza un test di switchover tra i nodi (ossia il PRIMARIO  diventa STANDBY e lo STANDBY diventa PRIMARIO ) per verificare che non ci siano effetti collaterali:

DGMGRL> switchover to lablbr;
Performing switchover NOW, please wait...
Operation requires a connection to instance "LABLBR" on database "lablbr"
Connecting to instance "LABLBR"...
Connected as SYSDBA.
New primary database "lablbr" is opening...
Oracle Clusterware is restarting database "lablar" ...
Switchover succeeded, new primary is "lablbr"

Verifichiamo che la tabella sia accessibile:

$ sqlplus system@lab
 
SQL:lab:>select instance_name from v$instance;
 
INSTANCE_NAME
----------------
LABLBR
 
SQL:lab:>SELECT COUNT(*) FROM appo_user.appo_tab;
 
  COUNT(*)
----------
    100000

Facciamo quindi uno switchover per ripristinare la situazione precedente:

DGMGRL> switchover to lablar;
Performing switchover NOW, please wait...
Operation requires a connection to instance "LABLAR1" on database "lablar"
Connecting to instance "LABLAR1"...
Connected as SYSDBA.
New primary database "lablar" is opening...
Oracle Clusterware is restarting database "lablbr" ...
Switchover succeeded, new primary is "lablar"
 

Verifichiamo che la tabella sia accessibile:

$ sqlplus system@lab
 
SQL:lab:>select instance_name from v$instance;
 
INSTANCE_NAME
----------------
LABLAR1
 
SQL:lab:>SELECT COUNT(*) FROM appo_user.appo_tab;
 
  COUNT(*)
----------
    100000
 

Conclusione:

In uno STANDBY database è possibile effettuare il ripristino di un datafile corrotto dal PRIMARIO senza difficoltà ed è molto utile soprattutto se la connessione tra i nodi è veloce mentre il backup è disponibile su un dispositivo molto più lento come un nastro.

Importante è fare attenzione a non eseguire la fase di RECOVER in quanto questa è automaticamente eseguita quando si riattiva la sincronizzazione tra i nodi.

Bibliografia:

https://dbaclass.com/article/restore-missing-datafile-in-standby-database-oracle-12c/

https://practicaloracle.wordpress.com/2012/04/02/how-to-create-block-corruption-in-asm-based-db/amp/

https://www.orafaq.com/wiki/Monitor_RMAN_restore