Como um dos pré-requisitos do Logical Standby é criá-lo a partir do Physical Standby, fiz um clone das máquinas que utilizei NESTE artigo, para investir melhor o tempo.
Confirmando que o primary está disponível:
[oracle@fornix1 ~]$ srvctl status database -d cortex
Database is running.
Checando o (até então) Physical Standby:
[oracle@fornix2 ~]$ srvctl status database -d cortexdr
Database is running.
Habilitando replicação entre os bancos:
[oracle@fornix2 ~]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Sat Mar 13 15:53:09 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle. All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT;
Database altered.
O Logical Standby possui algumas limitações de replicação que devem ser mapeadas antes de efetivamente ser criado. No banco Primary, é possível acessar a lista de objetos que não sejam suportados. No meu caso, como é um banco de laboratório, não há nada efetivamente rodando nele, e consequentemente nenhum objeto que não seja suportado:
[oracle@fornix1 ~]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Sat Mar 13 15:56:21 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle. All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> set pagesize 25
SQL> column owner format a5
SQL> SELECT DISTINCT OWNER, TABLE_NAME FROM DBA_LOGSTDBY_UNSUPPORTED ORDER BY OWNER,TABLE_NAME;
no rows selected
Criando uma tabela de teste:
SQL> CREATE TABLE SOE.BSS (COLUNA BFILE);
Table created.
Realizando consulta novamente:
SQL> set pagesize 25
SQL> column owner format a5
SQL> SELECT DISTINCT OWNER, TABLE_NAME FROM DBA_LOGSTDBY_UNSUPPORTED ORDER BY OWNER,TABLE_NAME;
OWNER
-----
TABLE_NAME
--------------------------------------------------------------------------------
SOE
BSS
Neste caso, podemos ver mais detalhes sobre a limitação, utilizando o comando abaixo:
SQL> SELECT COLUMN_NAME,DATA_TYPE FROM DBA_LOGSTDBY_UNSUPPORTED WHERE OWNER='SOE' AND TABLE_NAME= 'BSS';
COLUMN_NAME
--------------------------------------------------------------------------------
DATA_TYPE
--------------------------------------------------------------------------------
COLUNA
BFILE
O script abaixo também nos reporta as tabelas que possuem alguma limitações por datatype de colunas:
SQL> SELECT * FROM LOGSTDBY_UNSUPPORTED_TABLES;
OWNER
-----
TABLE_NAME
--------------------------------------------------------------------------------
SOE
BSS
Outra característica do Logical Standby, é que o mesmo não suporta schemas classificados como internos, vide exemplo abaixo:
SQL> SET PAGES 100
SQL> column owner format a25
SQL> SELECT OWNER FROM DBA_LOGSTDBY_SKIP WHERE STATEMENT_OPT = 'INTERNAL SCHEMA' ORDER BY 1;
OWNER
-------------------------
ANONYMOUS
APPQOSSYS
AUDSYS
CTXSYS
DBSFWUSER
DBSNMP
DIP
DVF
DVSYS
GGSYS
GSMADMIN_INTERNAL
GSMCATUSER
GSMROOTUSER
GSMUSER
LBACSYS
MDSYS
OJVMSYS
OLAPSYS
ORACLE_OCM
ORDDATA
ORDPLUGINS
ORDSYS
OUTLN
REMOTE_SCHEDULER_AGENT
SI_INFORMTN_SCHEMA
SYS
SYS$UMF
SYSBACKUP
SYSDG
SYSKM
SYSRAC
SYSTEM
WMSYS
XDB
XS$NULL
35 rows selected.
Tabelas que não tenham identificador único também não são suportadas pelo Logical, e podem ser encontradas na consulta abaixo:
SQL> COL OWNER FORMAT A30
SQL> COL TABLE_NAME FORMAT A80
SQL> SELECT OWNER, TABLE_NAME,BAD_COLUMN FROM DBA_LOGSTDBY_NOT_UNIQUE ;
OWNER TABLE_NAME B
------------------------------ -------------------------------------------------------------------------------- -
SOE WAREHOUSES N
SOE LOGON N
SOE PRODUCT_DESCRIPTIONS N
SOE ORDERENTRY_METADATA N
Todos os objetos que são expostos nas consultas dos passos acima devem ser tratados e avaliados, para que a criação do Logical Standby seja saudável, e logicamente, não trazer problemas futuro ao ambiente. Agora vamos efetivamente à construção do Logical:
Parando o Redo Apply no Standby:
SQL> ALTER SYSTEM SWITCH LOGFILE;
System altered.
SQL> !hostname
fornix1
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
Database altered.
SQL> !hostname
fornix2
No ambiente primary, precisamos rodar o comando abaixo para a criação do LogMiner Dictionary:
SQL> execute DBMS_LOGSTDBY.BUILD;
PL/SQL procedure successfully completed.
É recomandado também aumentar a retenção de UNDO em ambos ambientes:
SQL> SELECT INSTANCE_NAME,STATUS FROM V$INSTANCE;
INSTANCE_NAME STATUS
---------------- ------------
cortex OPEN
SQL> show parameter UNDO_RETENTION
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_retention integer 900
SQL> alter system set UNDO_RETENTION=3600 scope=both;
System altered.
SQL> SELECT INSTANCE_NAME,STATUS FROM V$INSTANCE;
INSTANCE_NAME STATUS
---------------- ------------
CORTEXDR MOUNTED
SQL> show parameter UNDO_RETENTION
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_retention integer 900
SQL> alter system set UNDO_RETENTION=3600 scope=both;
System altered.
Convertendo o Physical Standby para Logical Standby:
SQL> SHU IMMEDIATE;
ORA-01109: database not open
Database dismounted.
ORACLE instance shut down.
SQL> STARTUP MOUNT EXCLUSIVE;
ORACLE instance started.
Total System Global Area 2583690520 bytes
Fixed Size 8899864 bytes
Variable Size 553648128 bytes
Database Buffers 2013265920 bytes
Redo Buffers 7876608 bytes
Database mounted.
SQL> ALTER DATABASE RECOVER TO LOGICAL STANDBY CORTEXDR;
Database altered.
Baixando o standby e abrindo-o com resetlogs, além de iniciar o serviço de SQL apply:
SQL> SHU IMMEDIATE;
ORA-01507: database not mounted
ORACLE instance shut down.
SQL> STARTUP MOUNT;
ORACLE instance started.
Total System Global Area 2583690520 bytes
Fixed Size 8899864 bytes
Variable Size 553648128 bytes
Database Buffers 2013265920 bytes
Redo Buffers 7876608 bytes
Database mounted.
SQL> ALTER DATABASE OPEN RESETLOGS;
Database altered.
SQL> ALTER DATABASE START LOGICAL STANDBY APPLY IMMEDIATE;
Database altered.
Validando os status dos Online Redo Logs no standby:
SQL> SELECT THREAD#, SEQUENCE#, ARCHIVED, STATUS FROM V$LOG;
THREAD# SEQUENCE# ARC STATUS
---------- ---------- --- ----------------
1 1 NO CURRENT
1 0 YES UNUSED
1 0 YES UNUSED
Para validar a replicação, vou criar uma tabela nova com 1 registro no primary:
SQL> CREATE TABLE SOE.BSS1 (NOME VARCHAR2(50));
Table created.
SQL> ALTER TABLE SOE.BSS1 ADD PRIMARY KEY (NOME);
SQL> INSERT INTO SOE.BSS1 (NOME) VALUES ('BRUNO');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> ALTER SYSTEM SWITCH LOGFILE;
System altered.
Checando o Logical Standby:
SQL> DESC SOE.BSS1;
Name Null? Type
----------------------------------------- -------- ----------------------------
NOME NOT NULL VARCHAR2(50)
SQL> SELECT * FROM SOE.BSS1;
NOME
--------------------------------------------------
BRUNO
Por último, precisamos ajustar o atributo VALID_FOR dos 2 bancos, para que já fiquem configurados quando um switchover ocorrer.
Criando diretório de destino dos archived redo log files do standby redo logs:
ASMCMD> pwd
+DG_RECO/CORTEX/ARCHIVELOG
ASMCMD> !hostname
fornix1
ASMCMD> mkdir ARCHREDOLOG
ASMCMD> ls ARCHREDOLOG
Alterando parâmetros do primary:
[oracle@fornix1 ~]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Sun Mar 14 04:54:44 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle. All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> show parameter LOG_ARCHIVE_DEST_1
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_1 string LOCATION=+DG_RECO
log_archive_dest_10 string
log_archive_dest_11 string
log_archive_dest_12 string
log_archive_dest_13 string
log_archive_dest_14 string
log_archive_dest_15 string
log_archive_dest_16 string
log_archive_dest_17 string
log_archive_dest_18 string
log_archive_dest_19 string
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_1='LOCATION=+DG_RECO VALID_FOR=( ONLINE_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=CORTEX' scope=both;
System altered.
SQL> show parameter LOG_ARCHIVE_DEST_2
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_2 string SERVICE=cortexDR ASYNC VALID_F
OR=(ONLINE_LOGFILES,PRIMARY_RO
LE) DB_UNIQUE_NAME=cortexDR
log_archive_dest_20 string
log_archive_dest_21 string
log_archive_dest_22 string
log_archive_dest_23 string
log_archive_dest_24 string
log_archive_dest_25 string
log_archive_dest_26 string
log_archive_dest_27 string
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_28 string
log_archive_dest_29 string
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='LOCATION=+DG_RECO/CORTEX/ARCHIVELOG/ARCHREDOLOG/ VALID_FOR=(STANDBY_LOGFILES,STANDBY_ROLE) DB_UNIQUE_NAME=CORTEX' scope=both;
System altered.
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_3=ENABLE;
System altered.
Criando diretório no Standby:
ASMCMD> !hostname
fornix2
ASMCMD> pwd
+DG_RECO/CORTEXDR/ARCHIVELOG
ASMCMD> mkdir ARCHREDOLOG
Alterando parâmetros no standby:
[oracle@fornix2 ~]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Sun Mar 14 05:04:25 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle. All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> show parameter LOG_ARCHIVE_DEST_1;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_1 string LOCATION=+DG_RECO
log_archive_dest_10 string
log_archive_dest_11 string
log_archive_dest_12 string
log_archive_dest_13 string
log_archive_dest_14 string
log_archive_dest_15 string
log_archive_dest_16 string
log_archive_dest_17 string
log_archive_dest_18 string
log_archive_dest_19 string
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_1='LOCATION=+DG_RECO VALID_FOR=( ONLINE_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=CORTEXDR' scope=both;
System altered.
SQL> show parameter LOG_ARCHIVE_DEST_2;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_2 string SERVICE=CORTEX ASYNC DB_UNIQUE
_NAME=cortex VALID_FOR=(ONLINE
_LOGFILE,PRIMARY_ROLE)
log_archive_dest_20 string
log_archive_dest_21 string
log_archive_dest_22 string
log_archive_dest_23 string
log_archive_dest_24 string
log_archive_dest_25 string
log_archive_dest_26 string
log_archive_dest_27 string
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_28 string
log_archive_dest_29 string
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_2= 'SERVICE=CORTEX ASYNC NOAFFIRM delay=0 reopen=300 VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=CORTEX' SCOPE=BOTH ;
System altered.
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_2=ENABLE;
System altered.
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='LOCATION=+DG_RECO/CORTEXDR/ARCHIVELOG/ARCHREDOLOG/ VALID_FOR=(STANDBY_LOGFILES,STANDBY_ROLE) DB_UNIQUE_NAME=CORTEXDR' scope=both;
System altered.
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_3=ENABLE;
System altered.
SQL>
E finalmente, caso queiramos parar o serviço de replicação, basta rodar o comando abaixo no standby:
SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY;
Database altered.
Obs: Este procedimento foi criado pelo senhor Ahmed Baraka (www.ahmedbaraka.com) e foi apenas reproduzido por mim em um laboratório pessoal para fins de aprendizado.