Picture of Roberto Sobrinho
Roberto Sobrinho

30/05/2024

Monitoramento Proativo no Oracle Data Guard: Como Identificar Gaps + Script Pronto

Olá meninos e meninas, tudo bem por aí? Hoje falaremos a respeito de como identificar a analisar GAPS em ambiente de replicação Data Guard.

O Oracle Data Guard é uma ferramenta essencial para garantir a alta disponibilidade e a recuperação de desastres de bancos de dados. Um aspecto crítico da administração do Data Guard é monitorar os gaps entre o banco de dados primário e seus standbys. Esses gaps podem indicar problemas na replicação de dados e precisam de uma ação rápida do DBA..

O Que é um Gap?

Um gap no Data Guard ocorre quando um ou mais arquivos de redo log não foram aplicados ao banco de dados standby. Isso pode acontecer por várias razões, como problemas de rede, falhas no ambiente ou até mesmo uma configuração inadequada. Monitorar esses gaps é vital para garantir que o standby database esteja sempre atualizado com o primário.

Como Verificar Gaps no Data Guard

1. Visões Dinâmicas de Performance (V$ Views)

V$ARCHIVE_GAP

Exibe os gaps de arquivo log quando um processo de recuperação falha ao encontrar um arquivo log para aplicar. Execute no standby database:

SQL> 

SELECT * FROM V$ARCHIVE_GAP;

THREAD#   LOW_SEQUENCE#  HIGH_SEQUENCE#
-------   -------------  --------------
1         38866          38872
V$DATAGUARD_STATS

Fornece informações estatísticas sobre o Data Guard, incluindo gaps:

SQL> 

SELECT NAME, VALUE FROM V$DATAGUARD_STATS WHERE NAME LIKE 'transport lag' OR NAME LIKE 'apply lag';

NAME            VALUE
------------    -------------
transport lag   +00 00:05:30
apply lag       +00 00:02:15
V$MANAGED_STANDBY

Mostra o status atual dos processos de recuperação no standby database:

SQL> 

SELECT PROCESS, STATUS, THREAD#, SEQUENCE# FROM V$MANAGED_STANDBY WHERE PROCESS='MRP0';

PROCESS  STATUS   THREAD#  SEQUENCE#
-------  -------  -------  ---------
MRP0     APPLYING 1        1050

2. Alertas e Logs do Oracle

  • Alert Log: Verifique o alert log tanto do primário quanto do standby para mensagens sobre gaps e recuperação.
  • Trace Files: Arquivos de trace podem conter detalhes sobre problemas de gaps e aplicação de logs.

3. Script Pronto para Verificação de Gaps (dg.sql)

Para facilitar ainda mais a verificação dos gaps, estou disponibilizando um script (dg.sql) pronto que você pode utilizar. Esse script exibe os status de gaps, ajudando a manter seu standby database sempre em dia com o primário. Execute no banco de dados primário:

-- |
-- +-------------------------------------------------------------------------------------------+
-- | Objetivo   : GAP Replicacao Data Guard                                                    |
-- | Criador    : Roberto Fernandes Sobrinho                                                   |
-- | Data       : 30/05/2024                                                                   |
-- | Exemplo    : @dg.sql                                                                      |
-- | Arquivo    : dg.sql                                                                       |
-- | Referncia  :                                                                              |
-- | Modificacao: 1.1 - DBASobrinho - Ajustinho de nada, incluido o MAX                        |
-- +-------------------------------------------------------------------------------------------+
-- |                                                                https://dbasobrinho.com.br |
-- +-------------------------------------------------------------------------------------------+
-- |Peppa Pig diz: "Eu adoro pular em poças de Lama"
-- +-------------------------------------------------------------------------------------------+
SET TERMOUT OFF;
ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-YY HH24:MI:SS';
EXEC dbms_application_info.set_module( module_name => 'd[dg.sql]', action_name =>  'd[dg.sql]');
COLUMN current_instance NEW_VALUE current_instance NOPRINT;
SELECT rpad(sys_context('USERENV', 'INSTANCE_NAME'), 17) current_instance FROM dual;
COLUMN DG_CONF NEW_VALUE DG_CONF NOPRINT;
select value DG_CONF from v$parameter a where name = 'log_archive_config';
SET TERMOUT ON;
PROMPT
PROMPT +-------------------------------------------------------------------------------------------+
PROMPT | https://github.com/dbasobrinho/g_gold/blob/main/dg.sql                                    |
PROMPT +-------------------------------------------------------------------------------------------+
PROMPT | Script   : GAP Replicacao Data Guard                             +-+-+-+-+-+-+-+-+-+-+-+  |
PROMPT | Instancia: &current_instance                                     |d|b|a|s|o|b|r|i|n|h|o|  |
PROMPT | Versao   : 1.1                                                   +-+-+-+-+-+-+-+-+-+-+-+  |
PROMPT +-------------------------------------------------------------------------------------------+
PROMPT | DG_CONF  : &DG_CONF
PROMPT +-------------------------------------------------------------------------------------------+
PROMPT .
PROMPT .
SET ECHO        OFF
SET FEEDBACK    10
SET HEADING     ON
SET LINES       188
SET PAGES       300
SET TERMOUT     ON
SET TIMING      OFF
SET TRIMOUT     ON
SET TRIMSPOOL   ON
SET VERIFY      OFF
CLEAR COLUMNS
CLEAR BREAKS
CLEAR COMPUTES

COLUMN REPLICACAO_DG          FORMAT a100       HEAD 'REPLICACAO DG CONFIGURACAO'
COLUMN STANDBY_LAST_RECEIVED  FORMAT 9999999    HEAD 'ULTIMO ARC|ORIGEM'          justify CENTER
COLUMN STANDBY_LAST_APPLIED   FORMAT 9999999    HEAD 'ULTIMO ARC|DESTINO'         justify CENTER
COLUMN STANDBY_DT_LAST_APP    FORMAT a19        HEAD 'ULTIMA DATA|DESTINO'        justify CENTER
COLUMN data_atual             FORMAT a19        HEAD 'DATA| ATUAL'                justify CENTER
COLUMN MINUTOS                FORMAT 999999     HEAD 'DIF|MIN'                    justify CENTER
COLUMN ARC_DIFF               FORMAT 999999     HEAD 'DIF|ARC'                    justify CENTER
COLUMN DATABASE_ROLE          FORMAT a16        HEAD 'DATABASE|PERFIL'            justify CENTER
COLUMN PROTECTION_MODE        FORMAT a20        HEAD 'MODO|PROTECAO'              justify CENTER
COLUMN thread                 FORMAT 99999      HEAD 'THREAD|'                    justify CENTER
COLUMN SWITCHOVER_STATUS      FORMAT a16        HEAD 'SWITCHOVER|STATUS'          justify CENTER
COLUMN DEST_ID                FORMAT 999        HEAD 'ID|DEST'                    justify CENTER
COLUMN NAME                   FORMAT a10        HEAD 'DEST|NAME'                  justify CENTER
COLUMN ST                     FORMAT a03        HEAD 'ST|'                        justify CENTER
COLUMN NAME                   FORMAT a20        HEAD 'NAME|DESTINATION'           justify CENTER
SET COLSEP '|'
SET FEEDBACK    off
PROMPT +-------------------------------------------------------------------------------------------+
PROMPT | Status FUZZY Datafiles
PROMPT +-------------------------------------------------------------------------------------------+
COLUMN STATUS                FORMAT a10        HEAD 'STATUS'             justify CENTER
COLUMN FUZZY                 FORMAT a08        HEAD 'FUZZY'              justify CENTER
COLUMN CHECKPOINT_CHANGE     FORMAT a25        HEAD 'CHECKPOINT CHANGE'  justify CENTER 
COLUMN CHECKPOINT_TIME       FORMAT a25        HEAD 'CHECKPOINT TIME'    justify CENTER 
COLUMN CNT                   FORMAT 9999999    HEAD 'TOTAL'              justify CENTER 
select status,to_char(checkpoint_change#) checkpoint_change
      ,to_char(checkpoint_time, 'dd-mon-yyyy hh24:mi:ss') as checkpoint_time
      ,count(*) cnt ,fuzzy
  from v$datafile_header
 group by status,checkpoint_change#,checkpoint_time,fuzzy
 order by status,checkpoint_change#,checkpoint_time
/
SET FEEDBACK    on
PROMPT .
PROMPT .
PROMPT +-------------------------------------------------------------------------------------------+
PROMPT | Data Guard GAP Status
PROMPT +-------------------------------------------------------------------------------------------+
SET FEEDBACK    off
select  case when ARC_DIFF <= 3 then ':)'  when ARC_DIFF > 3 AND ARC_DIFF <=8  then ':|' ELSE ':('  END ST
,  z.DATABASE_ROLE, z.PROTECTION_MODE, z.SWITCHOVER_STATUS, z.name, z.thread, z.STANDBY_LAST_RECEIVED
,  z.STANDBY_LAST_APPLIED, z.STANDBY_DT_LAST_APP, /*z.data_atual,*/ z.MINUTOS, z.ARC_DIFF
from (
SELECT /*+ PARALLEL(8) */
       c.DATABASE_ROLE,
       c.PROTECTION_MODE,
       C.SWITCHOVER_STATUS,
           a.DEST_ID,
          (select max(nvl2(xx.name,xx.DEST_ID||' - '||xx.name,null))
             from v$archived_log xx
                where xx.DEST_ID = a.DEST_ID
                  and xx.resetlogs_change#=(SELECT resetlogs_change# FROM v$database)
                  and SEQUENCE#  = (select max(yy.SEQUENCE#)
                                      from v$archived_log yy
                                                         where yy.resetlogs_change#=(SELECT resetlogs_change# FROM v$database)
                                                          and yy.DEST_ID = xx.DEST_ID)) as name,
           a.thread# thread,
       b. last_seq STANDBY_LAST_RECEIVED ,
       a.applied_seq STANDBY_LAST_APPLIED,
       TO_CHAR(a.last_app_timestamp,'DD/MM/YYYY HH24:MI:SS') as STANDBY_DT_LAST_APP,
           TO_CHAR(sysdate,'DD/MM/YYYY HH24:MI:SS') as data_atual,
           (sysdate - a.last_app_timestamp) *24*60  as MINUTOS,
       b.last_seq - a.applied_seq ARC_DIFF
FROM   (SELECT /*+ PARALLEL(8) */
               DEST_ID,thread#,
               Max(sequence#) applied_seq,
               Max(next_time) last_app_timestamp
        FROM   gv$archived_log
        WHERE  applied = 'YES'
                and resetlogs_change#=(SELECT resetlogs_change# FROM v$database)
        GROUP  BY DEST_ID,thread#) a,
       (SELECT /*+ PARALLEL(8) */
                   DEST_ID,thread#,
               Max (sequence#) last_seq
        FROM   gv$archived_log
                where resetlogs_change#=(SELECT resetlogs_change# FROM v$database)
        GROUP  BY DEST_ID,thread#) b,
                (SELECT DATABASE_ROLE, DB_UNIQUE_NAME INSTANCE, OPEN_MODE, PROTECTION_MODE, PROTECTION_LEVEL, SWITCHOVER_STATUS FROM V$DATABASE) c
WHERE  a.thread# = b.thread#
and a.DEST_ID = b.DEST_ID) z
where UPPER(z.NAME) NOT LIKE '%ARCHIVELOG%'
and ((z.DATABASE_ROLE = 'PRIMARY'and (z.thread,z.DEST_ID ) in (SELECT INST_ID thread#, DEST_ID FROM GV$ARCHIVE_DEST_STATUS WHERE STATUS <> 'INACTIVE')) or (z.DATABASE_ROLE <> 'PRIMARY'))
order by z.name, z.thread
/
SET FEEDBACK    on
PROMPT .
PROMPT .

Exemplo de Output

SQL>@dg.sql

+-------------------------------------------------------------------------------------------+
| Data Guard GAP Status
+-------------------------------------------------------------------------------------------+

ST |    DATABASE    |        MODO        |   SWITCHOVER   |        NAME        |THREAD|ULTIMO ARC|ULTIMO ARC|    ULTIMA DATA    |  DIF  |  DIF
   |     PERFIL     |      PROTECAO      |     STATUS     |    DESTINATION     |      |  ORIGEM  | DESTINO  |      DESTINO      |  MIN  |  ARC
---|----------------|--------------------|----------------|--------------------|------|----------|----------|-------------------|-------|-------
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |2 - gina_stby       |     1|     38872|     38872|29/05/2024 16:51:47|     14|      0
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |2 - gina_stby       |     2|     42270|     42270|29/05/2024 16:51:49|     14|      0
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |2 - gina_stby       |     3|     41801|     41801|29/05/2024 16:51:52|     14|      0
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |2 - gina_stby       |     4|     42742|     42741|29/05/2024 16:45:40|     20|      1
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |2 - gina_stby       |     5|     42118|     42118|29/05/2024 16:51:51|     14|      0
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |3 - gina_app        |     1|     38872|     38872|29/05/2024 16:51:47|     14|      0
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |3 - gina_app        |     2|     42270|     42270|29/05/2024 16:51:49|     14|      0
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |3 - gina_app        |     3|     41801|     41801|29/05/2024 16:51:52|     14|      0
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |3 - gina_app        |     4|     42742|     42741|29/05/2024 16:45:40|     20|      1
:) |PRIMARY         |MAXIMUM PERFORMANCE |SESSIONS ACTIVE |3 - gina_app        |     5|     42118|     42118|29/05/2024 16:51:51|     14|      0
.
.

Explicação da Saída do Script

Vou detalhar cada coluna da saída do script para facilitar o entendimento:

  • ST: Indicação Smiley 🙂 OK 😐 Atenção 🙁 Problemas
  • DATABASE: Perfil do banco da dados (ex.: PRIMARY).
  • MODO: O modo de proteção configurado no Data Guard (ex.: MAXIMUM PERFORMANCE).
  • SWITCHOVER: O status da capacidade de switchover do banco de dados.
  • NAME: O nome do destino do standby (ex.: 2 – gina_stby).
  • THREAD: O número do thread associado ao banco de dados.
  • ULTIMO ARC ORIGEM: O último arquivo de archive log gerado no banco de dados primário.
  • ULTIMO ARC DESTINO: O último arquivo de archive log aplicado no banco de dados standby.
  • ULTIMA DATA DESTINO: A data e hora em que o último arquivo de archive log foi aplicado no standby.
  • DIF MIN: A diferença em minutos entre a geração do último archive log no primário e a aplicação no standby.
  • DIF ARC: A diferença em número de arquivos de archive log entre o primário e o standby.

Ação Rápida do DBA

Quando um gap é identificado, é crucial que o DBA tome medidas rápidas para resolver a discrepância. Isso pode incluir a verificação de problemas de rede, o reenvio manual de arquivos de redo log ou a reconfiguração de parâmetros do Data Guard.

Dicas para Gerenciar Gaps

  • Automatize a Verificação: Crie scripts que periodicamente verificam as visões relevantes e enviam alertas em caso de gaps.
  • Mantenha a Rede Saudável: Uma rede estável e bem configurada é crucial para minimizar gaps.
  • Aplique Patches Regularmente: Mantenha seu ambiente atualizado com os patches mais recentes para evitar problemas conhecidos que possam causar gaps.

Conclusão

Monitorar e gerenciar gaps no Oracle Data Guard é uma tarefa contínua que exige atenção e proatividade. Com o script de verificação de GAP fornecido neste post, você terá uma ferramenta poderosa para manter seu ambiente Oracle Data Guard sempre sincronizado. Este script não apenas exibe os gaps entre o banco de dados primário e os standby databases, mas também oferece insights valiosos para ajudar na gestão proativa desses ambientes críticos. Além disso, você pode incorporar o script em suas rotinas de monitoramento, automatizando o processo de verificação e garantindo que qualquer discrepância seja identificada e tratada rapidamente. Com essa abordagem, você estará fortalecendo a alta disponibilidade e a resiliência de sua infraestrutura de banco de dados.

#GuinaNãoTinhaDó #BóBó #CaceteDeAgulha #DBA #DataGuard #Oracle #AltaDisponibilidade #AdoroPularEmLama #FogoNoPavioOsManoInvadiu #EngeheiroDeObraPronta

Compartilhe

Facebook
Twitter
LinkedIn
WhatsApp
Email
Print

Pesquisar

Roberto Sobrinho

Sou Roberto Fernandes Sobrinho, também conhecido como Sobrinho DBA , pós graduado em “Architecture and Database Administration”, entusiasta, dedicado e com 20 anos de experiência com Oracle Database e suas diversas distribuições e variações.

Oracle ACE Associate

2025

Specialist

Exadata Database Machine X9M

Professional

Oracle Database Administration

Professional

Oracle Database 19c: RAC, ASM, & Grid Infra Administrator

Professional

Oracle Autonomous Database Cloud

Professional

Oracle Cloud Database Migration and Integration

Professional

Oracle Database PL/SQL Developer

Associate

Oracle Cloud Infrastructure Architect

Associate

Oracle Cloud Infrastructure Foundations

Categorias

Categorias

Tags

Monitoramento Proativo no Oracle Data Guard: Como Identificar Gaps + Script Pronto