作者:IT邦德
中国DBA联盟(ACDU)成员,目前从事DBA及程序编程
(Web\java\Python)工作,主要服务于生产制造
现拥有 Oracle 11g OCP/OCM、
Mysql、Oceanbase(OBCA)认证
分布式TBase\TDSQL数据库、国产达梦数据库以及红帽子认证
从业8年DBA工作,在数据库领域有丰富的经验
擅长主流数据Oracle、MySQL、PG 运维开发,备份恢复,安装迁移,性能优化、故障应急处理等。
文章目录
- 前言
- 1.相关知识
- 1.1 角色
- 1.2 角色管理服务
- 1.3 Switchover&Failover
- 2.实验目标
- 3.主备确认
- 3.1 主库确认
- 3.2 备库确认
- 4.意外宕机
- 4.1 主库模拟
- 4.2 备库查询
- 5.备库直接切换
- 6.切换后的测试
前言
最近客户搭建的DG,主库挂了,朋友邀请排查原因,做了一次DG failover, 此篇文档将DG failover做了详细的阐述!1.相关知识
1.1 角色
Oracle 数据库中含有两种角色。
用户角色:定义了一组权限的集合,该角色可以分配给用户,也可以分配给其他角色。
数据库角色:在备库中数据库扮演什么样的角色,primary还是standby。
v$database.DATABASE_ROLE标识了数据库的运行角色。
处于备库环境中,数据库有两种类型:phsical standby、logical standby。
1.2 角色管理服务
一个数据库运行在如下互相排斥的角色中。
Primary role:一个数据库运行在primary role,那么log transport services传递重做日志到备库。
Standby role:一个数据库运行在standby role,那么log apply services应用归档日志到备库。
角色管理服务允许用户动态地在主、备库中进行角色切换。
用户可以使用角色管理服务,进行主、备库的计划中的角色切换,
这个叫switchover,或者是非计划中的角色切换,叫failover。
1.3 Switchover&Failover
切换是在主数据库与其备数据库之间进行角色反转,切换确保不丢失数据。
这是对于主系统计划维护的典型操作。
在切换期间,主数据库转换到备角色,备数据库转换到主角色。
转换发生不需要重建任何数据库。
(1)Switchover
用到的场景:计划中的角色转换或用户操作系统和硬件的维护等。
(2)Failover
故障转移是当主数据库不可用时执行的。
故障转移只有在主数据库灾难故障的情况下执行,并且故障转移导致备数据库转换到主角色。
用到的场景:非计划中的角色切换,一般在紧急情况下使用。
根据保护模式的不同,可能会没有或者很少的数据损失。
(3)角色转换决策树
角色转换(switchover&failover)的最终目的是尽快地使主库在线,而同时尽量减少数据损失或者是实现无数据损失。尽量选择宕机时间最短,同时数据损失最小的策略。
总之在失败切换前,应该先考虑修复主数据库或者进行无数据损失的角色转换。
即使使用无数据损失的备库方案,修复主库可能会比切换到备库更快点。
如果修复了主库,那么就不需要修改客户端的连接。
但是如果修复工作导致了任何的数据损失,那么可能需要重新创建所有的备用数据库。
通常情况下,最合适切换的备库为已经应用了最多的归档日志的备用数据库。
2.实验目标
① 主库和物理dg的failover切换
② 还原为最初始的状态
注:failover切换后原来的主库将不可用,必须重新搭建,所以该实验请慎重选择。
3.主备确认
3.1 主库确认
SYS@PROD> set line 999
SYS@PROD> select name, LOG_MODE, OPEN_MODE, database_role, SWITCHOVER_STATUS, db_unique_name from v$database;

3.2 备库确认
SYS@PRODDG> alter database recover managed standby database using current logfile
disconnect from session;
SYS@PRODDG> set line 999
SYS@PRODDG> select name, LOG_MODE, OPEN_MODE, database_role, SWITCHOVER_STATUS, db_unique_name from v$database;
##备库应用日志同步取消
alter database recover managed standby database cancel;

4.意外宕机
4.1 主库模拟
SYS@PROD> select name, LOG_MODE, OPEN_MODE, database_role, SWITCHOVER_STATUS, db_unique_name from v$database;
SYS@PROD> alter system switch logfile;
SYS@PROD> set time on
19:08:09 SYS@PROD> set timing on
19:08:13 SYS@PROD> create table test as select * from dba_objects;
19:08:34 SYS@PROD> insert into test select * from test; --连续执行4次,再提交

4.2 备库查询
SYS@PRODDG> select count(1) from test;
SYS@PRODDG> select thread#, low_sequence#, high_sequence# from v$archive_gap;

如果没有发现明显的gap现象,说明此次的failover不会有数据损失情况。
在standby端,要进行关闭apply和结束应用动作。
5.备库直接切换
SYS@PRODDG> alter database recover managed standby database cancel;
SYS@PRODDG> alter database recover managed standby database finish;
SYS@PRODDG> select name, LOG_MODE, OPEN_MODE, database_role, SWITCHOVER_STATUS, db_unique_name from v$database;
SYS@PRODDG> alter database commit to switchover to primary with session shutdown;
SYS@PRODDG> select name, LOG_MODE, OPEN_MODE, database_role, SWITCHOVER_STATUS, db_unique_name from v$database;
SYS@PRODDG> alter database open;
SYS@PRODDG> select name, LOG_MODE, OPEN_MODE, database_role, SWITCHOVER_STATUS, db_unique_name from v$database;

切换成功。切换之后,我们观察这个过程的日志情况
[oracle@source ~]$ cd /u01/app/oracle/diag/rdbms/11g_st/PRODDG/trace
[oracle@source trace]$ tail -1000f alert_PRODDG.log

6.切换后的测试
SYS@PRODDG> alter system switch logfile;
SYS@PRODDG> delete from test where rownum<=10000;
SYS@PRODDG> commit;
SYS@PRODDG> set line 999
SYS@PRODDG> select name, LOG_MODE, OPEN_MODE, database_role, SWITCHOVER_STATUS, db_unique_name from v$database;

SYS@PRODDG> set line 9999
SYS@PRODDG> col DEST_NAME format a20
SYS@PRODDG> col DESTINATION format a20
SYS@PRODDG> col GAP_STATUS format a10
SYS@PRODDG> col DB_UNIQUE_NAME format a10
SYS@PRODDG> col error format a10
SYS@PRODDG>
SELECT al.thread#,
ads.dest_id,
ads.DEST_NAME,
(SELECT ad.TARGET
FROM v$archive_dest AD
WHERE AD.DEST_ID = ADS.DEST_ID) TARGET,
ADS.DATABASE_MODE,
ads.STATUS,
ads.error,
ads.TYPE,
ads.RECOVERY_MODE,
ads.DB_UNIQUE_NAME,
ads.DESTINATION,
ads.GAP_STATUS,
(SELECT MAX(sequence#) FROM v$log na WHERE na.thread# = al.thread#) "Current Sequence",
MAX(sequence#) "Last Archived",
MAX(decode(al.APPLIED, 'YES', sequence#)) APPLIED_SEQ#
FROM (SELECT *
FROM v$archived_log V
WHERE V.resetlogs_change# =
(SELECT d.RESETLOGS_CHANGE# FROM v$database d)) al,
v$archive_dest_status ads
WHERE al.dest_id(+) = ads.dest_id
AND ads.STATUS != 'INACTIVE'
GROUP BY al.thread#,
ads.dest_id,
ads.DEST_NAME,
ads.STATUS,
ads.error,
ads.TYPE,
ADS.DATABASE_MODE,
ads.RECOVERY_MODE,
ads.DB_UNIQUE_NAME,
ads.DESTINATION,
ads.GAP_STATUS
ORDER BY al.thread#,
ads.dest_id;

此时,failover过程成功。
