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: ¤t_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