1. 前言

目前接触的项目是有对以oracle数据库的服务跨机房灾备/读压力分载这样的需求。从成本、需求等方面考虑,暂定为用oracle企业版免费提供的data guard(下称DG)操作实现跨机房灾备。

上有业务层的切换这里不做说明,仅说明DG作为灾备的方案如何选择合适的冷备方案。

另外,对于oracle11g版本的DG,可在备份数据的同时提供只读服务。

下面对DG说明它的工作方式,并附上简单部署实例。

2. Data Guide 用来做什么

提及Oracle数据同步、复制、备份等等这些关键字,其实Oracle本身作为商业型数据库就提供了很多的工具。但是侧重点不同,这里结合查询的资料大概说下,也可以作为实际环境中解决问题时一点参考。

RAC(real application clusters):实时应用集群,强项在于解决单点故障和负载均衡,因此RAC方案常用于7*24的核心系统,但RAC方案中的数据只有一份,尽管可以通过RAID等机制可以避免存储故障,但是数据本身是没有冗余的,容易形成单点故障。,比较适合短距离(单机房)内保障应用可用性。

DG:通过冗余数据来提供数据保护,通过日志同步机制保证冗余数据和主数据之前的同步,这种同步可以是实时,延时,同步,异步多种形式。Data Gurad常用于异地容灾和小企业的高可用性方案. Oracle 11g版本中增强物理Standby的应用功能,在11g版本中,物理Standby可以在OPEN READ ONLY模式下继续应用REDO数据,这就极大地提升了物理Standby数据库的应用场合。+

3. Data Guide 如何工作

名词解释:
Primary Database	主库
Standy Database		备库
Redo Log			Oracle Redo Log 实时日志

3.1 架构

日志就是数据库操作的记录

DG架构按功能可分为3部分:

  • 日志发送(Redo Send)
  • 日志接收(Redo Receive)
  • 日志应用 (Redo Apply)

3.1.1 日志发送

Primary Database运行过程中,会源源不断地产生Redo日志,这些日志需要发送到Standy Database

DG支持2种方式发送,一种通过ARCH进程发送归档日志,一种通过LGWR发送实时日志。

可以看到都会占用数据库额外的一部分资源完成这项工作。

3.1.1.1 使用ARCH进程

这是默认方式,即主库日志归档时通过ARCH进程发送归档日志给备库。基本流程如下:

  1. Primary Database不断产生Redo Log,这些日志被LGWR进程写到联机日志
  2. 当一组联机日志被写满后,会发生日志切换(Log Switch)并且会触发本地归档,本地归档位置是采用LOG_ARCHIVE_DEST_1='LOCATION=/path'这种格式定义的
  3. 完成本地归档后,联机日志就可以被覆盖重用
  4. ARCH 进程通过Net把归档日志发送给Standby DatabaseRFS(Remote File Server)进程
  5. Standby Database端的RFS进程把接收的日志写入到归档日志
  6. Standby Database端的MRP(Managed Recovery Process)进程(Redo Apply)或者LSP 进程(SQL Apply)Standby Database上应用这些日志,进而同步数据

可以看到,Primary Database只有在发生归档时才会发送日志到Standby Database,而且不写入备机的重做日志(Standby Redologs)。所以,如果Primary Database异常宕机,联机日志中的Redo内容就会丢失,因此使用ARCH进程无法避免数据丢失的问题。

要想避免数据丢失,就必须使用LGWR

3.1.1.2 使用LGWR线程1

使用LGWR 又分SYNC(同步)和ASYNC(异步)两种方式。

3.1.1.2.1 SYNC同步方式

同步方式即主库的事务得确认日志写入成功才会提交。它的流程如下:

  1. Primary Database 产生的Redo 日志要同时写到联机日志文件和本地的LNSn进程(LGWR Network Server Process),再由LNSn(LGWR Network Server process)进程把日志通过网络发送给远程的目的地,每个远程目的地对应一个LNS进程,多个LNS进程能够并行工作
  2. LGWR 必须等待写入本地日志文件操作和通过LNSn进程的网络传送都成功,Primary Database上的事务才能提交,这也是SYNC的含义所在。
  3. Standby DatabaseRFS进程把接收到的日志写入到Standby Redo Log日志中。
  4. Primary Database的日志切换也会触发Standby Database上的日志切换,即Standby DatabaseStandby RedoLog的归档,然后触发Standby DatabaseMRP或者LSP进程恢复归档日志。

使用LGWR SYNC方式时,还可以同时使用NET_TIMEOUT参数,这个参数单位是秒,代表如果多长时间内网络发送没有响应,LGWR 进程会抛出错误。示例如下:(抛出错误不意味着失败,可以设置重试发送或者等待下一次归档时再次发送 2

 alter system set log_archive_dest_2 = 'SERVICE=ST  LGWR  SYNC  NET_TIMEOUT=30' scope=both;

Question: 这里主库日志切换是redo日志组满了,备库那边的日志组是什么情况,和主库一样?

如果某次同步挂了,理论上备库的日志组大小要大于主库的日志组文件大小?另外下一次归档发送时主库日志一部分已归档,如何发送?

Reply:备库是用主库生成的standby controlfile建立实例的,日志组和主库一样。如何发送待测试:先停掉备库,主库操作不断归档日志,再开启备库看看同步情况

对于归档失败的处理,LOG_ARCHIVE_DEST_n参数有几个属性,可以用来控制归档过程中出现故障时应该采取的措施。

REOPEN 
	指定时间后再次尝试归档,默认300s
MAX_FAILURE 
	控制失败尝试次数
LOG_ARCHIVE_DEST_1='LOCATION=E:/ora10g/oradata/jsspdg/ REOPEN=100 MAX_FAILURE=3'  

ALTERNATE
	指定替补的归档目的地
	LOG_ARCHIVE_DEST_1='LOCATION=/disk1 ALTERNATE=LOG_ARCHIVE_DEST_2'
	
// REOPEN优先级更高,先重试再替补
3.1.1.2.2 ASYNC异步方式

显然同步方式受网络影响,而且比较影响主库性能。超时时间设置也要考虑网络延迟的影响。不过同步方式能有效的保证数据准确性。

异步方式不要求网络日志写入成功,基本流程如下:

  1. Primary Database 一段产生Redo 日志后,LGWR 把日志同时提交给日志文件和本地LNS 进程,但是LGWR进程只需成功写入日志文件就可以,不必等待LNSn进程的网络传送成功
  2. LNSn进程异步地把日志内容发送到Standby Database。多个LNSn进程可以并发发送
  3. Primary DatabaseOnline Redo Log 写满后发生Log Switch触发归档操作,也触发Standby DatabaseStandby Redo Log 的归档;然后触发MRP或者LSP 进程恢复归档日志

3.1.2 日志接收

Standby DatabaseRFS(Remote File Server)进程接收到日志后,就把日志写到Standby Redo Log或者Archived Log文件中。具体写入哪个文件,取决于Primary的日志传送方式和Standby database的位置。

3.1.3 日志应用

日志应用服务,就是在Standby Database上重演Primary Database日志,从而实现两个数据库的数据同步。

根据Standby Database重演日志方式的不同,可分为物理Standby(Physical Standby)和逻辑Standby(Logical Standby)

根据Redo Apply发生的时间可以分成两种: 实时应用(Real-Time Apply)和归档时应用。

物理Standby:
	接收完Primary数据库生成的REDO数据后,以介质恢复的方式实现同步,这种方式也叫Redo Apply。没有数据类型的限制,可以保证两个数据库完全一致。只能在Mount 状态下进行恢复,11g版本后可以以同时只读方式打开。

逻辑Standby:
	接收后将其转换成SQL语句,在Standby数据库上执行SQL语句实现同步,这种方式叫SQL Apply。不支持所有数据类型,可以在视图DBA_LOGSTDBY_UNSUPPORTED 中查看不支持的数据类型,如果使用了这种数据类型,则不能保证数据库完全一致。可以在恢复的同时进行读写操作。

创建逻辑Standby数据库要先创建一个物理Standby数据库,然后再将其转换成逻辑Standby数据库

Physical DG转换Logical DG

实时应用(Real-Time Apply),:
	这种方式必须Standby Redo Log,每当日志被写入Standby Redo Log时,就会触发恢复,使用这种方式的好处在与可以减少数据库切换(Switchover 或者Failover)的时间,因为切换时间主要用在剩余日志的恢复上。 
	
归档时应用:
	这种方式在Primary Database发生日志切换,触发Standby Database 归档操作,归档完成后触发恢复。 这也是默认的恢复方式。
-- Physical Standby启用Real-Time:
Alter database recover managed standby database using current logfile;

-- Logical Standby启用Real-Time:
Alter database start logical standby apply immediate;

-- 查看是否使用Real-Time apply:
Select recovery_mode from v$archive_dest_status;

某些情况下你可能不希望Standby数据库Primary太过同步,那就可以在Primary数据库端发送REDO数据的相应LOG_ARCHIVE_DEST_n参数中指定DELAY属性(单位为分钟,如果指定了DELAY属性,但没有指定值,则默认是30分钟)。

ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='SERVICE=DavePrimary ARCH VALID_ FOR=  
(ONLINE_LOGFILES, PRIMARY_ROLE) DB_UNIQUE_NAME=Dave DELAY=15'; 

-- 物理Standby可以通过下列语句取消延迟应用:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE NODELAY; 

-- 逻辑Standby可以通过下列语句取消延迟应用:
ALTER DATABASE START LOGICAL STANDBY APPLY NODELAY; 

3.2 数据保护模式

Data Guard 允许定义3钟数据保护模式:

  • 最大保护(Maximum Protection)
  • 最大可用(Maximum Availability)
  • 最大性能(Maximum Performance)

3.2.1 最大保护

要求所有的事务在提交前其REDO不仅被写入到本地的Online Redologs,还要同时写入到Standby数据库Standby Redologs,并确认REDO数据至少在一个Standby数据库中可用(如果有多个的话),然后才会在Primary数据库上提交。如果出现了什么故障导致Standby数据库不可用的话(比如网络中断),Primary数据库会被Shutdown,以防止数据丢失。

可以看到,必须有一个备库同步成功,主库事务才提交。如果只有一个备库,是完全保证主备库数据一致的。

结合前面我们知道这种方式也就是使用LGWR线程同步发送并且AFFIRM

3.2.2 最高可用

与最大保护模式类似,也是要求本地事务在提交前必须至少写入一台Standby数据库的Standby Redologs中,不过与最大保护模式不同的是,如果出现故障导致Standby数据库无法访问,Primary数据库并不会被Shutdown,而是自动转为最高性能模式,等Standby数据库恢复正常之后,Primary数据库又会自动转换成最高可用性模式。

结合前面我们知道这种方式也是使用LGWR线程同步发送并且AFFIRM 3

Question: 这里怎么设置的?和最大保护如何区别,如果不设置超时时间,怎么判定备库不可用,而此时主库如何做?设置超时时间也是类似,超时是shutdown还是commit?

参见[设置保护模式](#3.2.4 设置保护模式)

3.2.3 最高性能

默认方式,归档方式或异步方式发送日志,事务的提交不依赖于网络日志的写入 4

可以LGWR ASYNC 或者ARCH

Question:这里事务提交会确认本地日志写成功吗?应该是的,否则就很容易出问题

3.2.4 设置保护模式

上面的疑问可以取消了,数据库有参数设置的。

  1. 关闭数据库,重启到Mount 状态,如果是RAC,需要关闭所有实例,然后只启动一个实例到mount状态。

  2. 修改模式:

    -- {PROTECTION | AVAILABILITY | PERFORMANCE}
    ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE PERFORMANCE; 
    
  3. 打开数据库: alter database open;

  4. 确认修改数据保护模式

    select protection_mode,protection_level from v$database; 
    

3.3 裂缝检测和解决

Primary Database的某些日志没有成功发送到Standby Database,这时候发生了归档裂缝(Archive Gap)。缺失的日志就是Gap。

3.3.1 自动检测解决

Data Guard能够自动检测,解决归档裂缝,不需要DBA的介入。这需要备库配置FAL_CLIENTFAL_SERVER 这两个参数。

FAL_CLIENTFAL_SERVER两个参数都是Oracle Net NameFAL_CLIENT 通过网络向FAL_SERVER发送请求,FAL_SERVER通过网络向FAL_CLIENT发送缺失的日志。FAL_CLIENTFAL_SERVER发送请求时,会携带FAL_CLIENT参数值,用来告诉FAL_SERVER应该向哪里发送缺少的日志。 这个参数值也是一个Oracle Net Name,这个Name是在FAL_SERVER上定义的,用来指向FAL_CLIENT.

3.3.2 手工检测解决

  1. 查看是否有日志GAP:

    SELECT UNIQUE THREAD#, MAX(SEQUENCE#) OVER(PARTITION BY THREAD#) LAST FROM V$ARCHIVED_LOG; 
    
    SELECT THREAD#, LOW_SEQUENCE#, HIGH_SEQUENCE# FROM V$ARCHIVE_GAP; 
    
  2. 如果有,则拷贝过来

  3. 手工的注册这些日志:

    ALTER DATABASE REGISTER LOGFILE '路径'; 
    

3.4 指定日志发送对象

3.4.1 VALID_FOR

LOG_ARCHIVE_DEST_n参数中的VALID_FOR属性,用来指定传输的内容,该属性有两个参数值需要指定:REDO_LOG_TYPE和DATABASE_ROLE。该参数的主要目的是为了确保,一旦发生角色切换操作后数据库的正常运转。

REDO_LOG_TYPE:
 	ONLINE_LOGFILE、STANDBY_LOGFILE、ALL_LOGFILES。  

DATABASE_ROLE:
	PRIMARY_ROLE、STANDBY_ROLE、ALL_ROLES。 

3.4.2 DB_UNIQUE_NAME

DB_UNIQUE_NAME作用是指定唯一的Oracle数据库名称,保证REDO数据在传输过程中才能确认传输到DBA希望被传输到的数据库上。

要确保REDO数据被传输到指定服务器,除了在LOG_ARCHIVE_DEST_n参数中指定正确DB_UNIQUE_NAME属性之外,还有一个初始化参数LOG_ARCHIVE_CONFIG也需要进行正确的配置。该参数除了指定Data Guard环境中的唯一数据库名外,还包括几个属性,用来控制REDO数据的传输和接收:

-- SEND:允许数据库发送数据到远端。
-- RECEIVE:允许Standby接收来自其他数据库的数据。
-- NOSEND,NORECEIVE:自然就是禁止喽。
LOG_ARCHIVE_CONFIG='NORECEIVE,DG_CONFIG= (PRI,ST) ' 

4. 示例

Oracle 11gR2 DG部署(RMAN方式)

本机使用docker完成实例,后面尽可能复现些异常情况。

借助与docker容器占用轻量资源以及容器间暴露的端口默认互通,数据库之间的网络是连通的。

这里把整个操作流程总结下。

4.1 资源准备

hostnameipdb_namedb_unique_namenet service name
db1172.17.0.2orclprimaryorcl
db2172.17.0.3orclstandbyorcl

4.1.1 安装oracle

4.1.1.1 下载oracle11g镜像
docker pull registry.cn-hangzhou.aliyuncs.com/jydsun/oracle11g_ssg:1.04

登陆账号:root/install oracle/install

实例密码:sys/oracle

其实都用不着,在docker里直接获取到root权限,切换oracle用户就有sysdba权限

4.1.1.2 启动容器
# primary
docker run -d --name db1 -p 15211:1521  -v /mnt/hgfs/E/Workspace/Database/Oracle/data/db1:/data/oracle_data -e oracle_allow_remote=true registry.cn-hangzhou.aliyuncs.com/jydsun/oracle11g_ssg:1.04
# standby
docker run -d --name db2 -p 15212:1521 -v /mnt/hgfs/E/Workspace/Database/Oracle/data/db2:/data/oracle_data -e oracle_allow_remote=true registry.cn-hangzhou.aliyuncs.com/jydsun/oracle11g_ssg:1.04

4.2 DG部署

另外了解到oracle对dg也是有个管理工具DG_Broker的,可以帮助dba快速部署dg,这里给个链接了解下 DG_Broker配置

以下是手工部署的过程,使用broker更简单点,但其实最终也是帮助生成以下过程中的参数设置等等,所以这里展示下手工的全过程,帮助了解每个细节点。(其实是之前根本不知道db_broker,后来才知道有这玩意,不然我也想先用broker啊…后面再补充个文档吧,o(╥﹏╥)o)

4.2.1 开启主库归档并设置强制日志

--干净的关闭数据库
SQL> shutdown immediate
--以mount模式启动
SQL> startup mount
--切换到归档模式
SQL> alter database archivelog;
--开启强制日志
SQL> alter database force logging;
--打开数据库
SQL> alter database open;
--查看归档
SQL> archive log list;
--查看是否为强制日志
SQL> select force_logging from v$database;

4.2.2 添加主库Standby Redo Log

--查看Redo和Standby Redo
SQL> select * from v$logfile;									
--仅仅显示Online Redo,不显示Standby Redo
SQL> select * from v$log;										
--新增一组大小为500M的Standby Redo,这里的group号不得与Online redo重复,大小和online redo 一样,每个实例Standby Redo数量比online redo 多一个
SQL> alter database add standby logfile group 21 '/u01/app/oracle/oradata/orcl/standby21.log' size 500M;
SQL> alter database add standby logfile group 22 '/u01/app/oracle/oradata/orcl/standby22.log' size 500M;
SQL> alter database add standby logfile group 23 '/u01/app/oracle/oradata/orcl/standby23.log' size 500M;
SQL> alter database add standby logfile group 24 '/u01/app/oracle/oradata/orcl/standby24.log' size 500M;

4.2.3 设置主库参数并启动

-- 创建pfile,默认保存在$ORACLE_HOME/dbs/initorcl.ora,也可以指定路径pfile='/path'
SQL> create pfile from spfile;

-- 修改主库初始化参数,在pfile后面追加
*.db_unique_name='primary'
*.service_names='orcl'
*.fal_client='primary'
*.fal_server='standby'
*.log_archive_config='DG_CONFIG=(primary,standby)'
*.log_archive_dest_1='location=/data/oracle_data/dataguard/log valid_for=(all_logfiles,all_roles) db_unique_name=primary'
*.log_archive_dest_2='service=standby lgwr async valid_for=(online_logfiles,primary_role) db_unique_name=standby' 
*.log_archive_dest_state_1='enable'
*.log_archive_dest_state_2='enable'
*.log_archive_format='%t_%s_%r.dbf'
*.DB_FILE_NAME_CONVERT='/opt/oracle/app/oradata/orcl','/opt/oracle/app/oradata/orcl'
*.LOG_FILE_NAME_CONVERT='/data/oracle_data/dataguard/log','/data/oracle_data/dataguard/log'
*.standby_file_management='auto'

-- 创建新的spfile并重启
SQL> shutdown immediate
SQL> create spfile from pfile;
SQL> startup

下列参数为Primary角色相关的初始化参数

DB_NAME注意保持同一个Data Guard中所有数据库DB_NAME相同 例如:DB_NAME=Dave
DB_UNIQUE_NAME为每一个数据库指定一个唯一的名称,该参数一经指定不会再发生变化,除非DBA主动修改它 例如:DB_UNIQUE_NAME=DavePre
LOG_ARCHIVE_CONFIG该参数用来控制从远端数据库接收或发送REDO数据,通过DG_CONFIG属性罗列同一个Data Guard中所有DB_UNIQUE_NAME(含Primary数据库和Standby数据库),以逗号分隔,SEND/NOSEND属性控制是否可以发送,RECEIVE/NORECEIVE属性控制是否能够接收 例如:LOG_ARCHIVE_CONFIG=‘DG_CONFIG=(DavePre,DaveDG)’
LOG_ARCHIVE_DEST_n归档文件的生成路径。该参数非常重要,并且属性和子参数也特别多(可以直接查询Oracle官方文档。Data Guard白皮书第14章专门介绍了该参数各属性及子参数的功能和设置)。例如: LOG_ARCHIVE_DEST_1=‘LOCATION=l:/oracle/oradata/Dave VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=DavePre’
LOG_ARCHIVE_DEST_STATE_n是否允许REDO传输服务传输REDO数据到指定的路径。该参数共拥有4个属性值,功能各不相同。
REMOTE_LOGIN_PASSWORDFILE推荐设置参数值为EXCLUSIVE或者SHARED,注意保证相同Data Guard配置中所有DB服务器SYS密码相同

以下参数为与Standby角色相关的参数(建议在Primary数据库的初始化参数中也进行设置,这样即使发 生角色切换,新的Standby也能直接正常运行)

FAL_SERVER指定一个Net服务名,该参数值对应的数据库应为Primary角色。当本地数据库为Standby角色时,如果发现存在归档中断的情况,该参数用来指定获取中断的归档文件的服务器 例如:FAL_SERVER=DavePre 提示:FAL是Fetch Archived Log的缩写 FAL_SERVER参数支持多个参数值的哟,相互间以逗号分隔
FAL_CLIENT又指定一个Net服务名,该参数对应数据库应为Standby角色。当本地数据库以Primary角色运行时,向参数值中指定的站点发送中断的归档文件 例如:FAL_CLIENT=DaveDG FAL_CLIENT参数也支持多个参数值,相互间以逗号分隔。
DB_FILE_NAME_CONVERTStandby数据库的数据文件路径与Primary数据库数据文件路径不一致时,可以通过设置DB_FILE_NAME_CONVERT参数的方式让其自动转换。该参数值应该成对出现,前面的值表示转换前的形式,后面的值表示转换后的形式 例如:DB_FILE_NAME_CONVERT=‘f:/oradata/DavePre’,‘l:/oradata/DaveDG’
LOG_FILE_NAME_CONVERT使用方式与上相同,只不过LOG_FILE_NAME_CONVERT专用来转换日志文件路径 例如: LOG_FILE_NAME_CONVERT=‘f:/oradata/DavePre’,‘l:/oradata/DaveDG’
STANDBY_FILE_MANAGEMENT如果Primary数据库数据文件发生修改(如新建、重命名等)则按照本参数的设置在Standby数据库中作相应修改。设为AUTO表示自动管理。设为MANUAL表示需要手工管理 例如:STANDBY_FILE_MANAGEMENT=AUTO

4.2.4 设置主库监听

注意细节:

  • ORACLE_HOME 最后不要/
  • listener.ora 的sid_name和tnsnames.ora sid_name一样

太难受了…

# 修改listener.ora /app/product/11.2.0/dbhome_1/network/admin/listener.ora,

# listener.ora Network Configuration File: C:\app\Administrator\product\11.2.0\dbhome_1\network\admin\listener.ora
# Generated by Oracle configuration tools.

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC = 
    	(SID_NAME = orcl) 
    	(ORACLE_HOME = /opt/oracle/app/product/11.2.0/dbhome_1)
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.2)(PORT = 1521))
    )
  )

ADR_BASE_LISTENER = /opt/oracle/app/admin
# tnsnames.ora 增加primary和standby

# tnsnames.ora Network Configuration File: C:\app\Administrator\product\11.2.0\dbhome_1\network\admin\tnsnames.ora
# Generated by Oracle configuration tools.

ORACLR_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
    (CONNECT_DATA =
      (SID = CLRExtProc)
      (PRESENTATION = RO)
    )
  )

ORCL =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.2)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
  
#primary
PRIMARY =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.2)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
#standby
STANDBY=
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.3)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
# 重启监听服务
lsnrctl stop
lsnrctl start

4.2.5 复制主库配置到从库

  • 将主库oracle目录下的admin,cfgtollogs,diag,flash_recover_area目录拷贝到备用库的相同路径
  • 将主库oracle目录下的oradata文件夹下内容复制到从库相同目录下 。
  • 将主库生成的密码文件拷贝到从库相同路径下
  • 将D:\app\interlib拷贝到备库相同路径
  • 将standby.ctl文件拷贝到备库的数据文件夹内(D:\app\Administrator\oradata\orcl\)和D:\app\Administrator\flash_recovery_area\orcl\下。在两个路径下将文件复制为control01.ctl,control02.ctl,control03.ctl。
  • 将主库的listener.ora和tnsname.ora拷贝到备库相同路径 。
  • 主库和备库创建D:\app\interlib\tmp文件夹。

注:通过rman先恢复时可以只创建文件夹,standby controlfile可以不用创建

我这里因为是docker直接创建的容器实例,所以这些东西都有了,问题不大

# 复制主库密码到备库(sys用户密码设置一致)
cd /u01/app/oracle/product/11.2.0/dbhome_1/dbs/
scp orapworcl db2:/u01/app/oracle/product/11.2.0/dbhome_1/dbs/

# 复制pfile
cd /u01/app/oracle/product/11.2.0/dbhome_1/dbs/
scp initorcl.ora db2:/u01/app/oracle/product/11.2.0/dbhome_1/dbs/

# 复制监听文件listener.ora和tnsnames.ora
cd /u01/app/oracle/product/11.2.0/dbhome_1/network/admin/
scp listener.ora db2:/u01/app/oracle/product/11.2.0/dbhome_1/network/admin/

cd /u01/app/oracle/product/11.2.0/dbhome_1/network/admin/
scp tnsnames.ora db2:/u01/app/oracle/product/11.2.0/dbhome_1/network/admin/

# 备库新建实例
oradim -new -sid orcl

# 主库创建密码
# orapwd file=$ORACLE_HOME/dbs/orapworcl password=oracle entries=10 ignorecase=Y

# 这里用db1,db2应该要配hosts的,应该可以用ip替代
# 这里测试时用docker映射在主机上,所以直接在windows下操作日志文件部分的复制

4.2.6 设置备库参数

-- 修改备库初始化参数
*.db_unique_name='standby'
*.service_names='orcl'
*.fal_client='standby'
*.fal_server='primary'
*.log_archive_config='DG_CONFIG=(primary,standby)'
*.log_archive_dest_1='location=/data/oracle_data/dataguard/log valid_for=(all_logfiles,all_roles) db_unique_name=standby'
*.log_archive_dest_2='service=primary lgwr async valid_for=(online_logfiles,primary_role) db_unique_name=primary' 
*.log_archive_dest_state_1='enable'
*.log_archive_dest_state_2='enable'
*.log_archive_format='%t_%s_%r.dbf'
*.DB_FILE_NAME_CONVERT='/opt/oracle/app/oradata/orcl','/opt/oracle/app/oradata/orcl'
*.LOG_FILE_NAME_CONVERT='/data/oracle_data/dataguard/log','/data/oracle_data/dataguard/log'
*.standby_file_management='auto'

4.2.7 创建备库目录

使用oracle用户,(复制文件夹)

mkdir -p /u01/app/oracle/oradata/orcl/
mkdir -p /u01/app/oracle/admin/orcl/adump/
mkdir -p /u01/app/oracle/fast_recovery_area/orcl/

# 还包括和主库一样的归档日志文件夹等

4.2.8 设置备库监听

修改备库listener.ora和tnsnames.ora

# 验证监听
tnsping primary
tnsping standby

sqlplus sys/oracle@primary as sysdba

4.2.9 启动备库nomount

-- 创建备库spfile
SQL> create spfile from pfile;
-- nomount挂载
SQL> startup nomount;

4.2.10 恢复主库数据(全量)

DG是日志的同步,在开启归档之前的数据还是要全量备份到备库上。可以用impdp逻辑备份,也可以rman物理备份。(以前用expdp/impdp比较多,也可以通过数据库连接远程备份,对rman其实还不怎么了解,了解一下)

# 在备库执行,连接目标库和辅助数据库
rman target sys/oracle@primary auxiliary sys/oracle@standby

# 恢复数据文件,standby控制文件,standby日志组
RMAN> duplicate target database for standby from active database nofilenamecheck;

# 注意如果没有用rman,控制文件要手动建的,在主库创建standby controlfile,并复制为备库启动的controlfile
# alter database create standby controlfile as '/data/oracle_data/dataguard/standby.ctl';
# scp /data/oracle_data/dataguard/standby.ctl db2:/data/oracle_data/dataguard/standby.ctl
# cp /data/oracle_data/dataguard/standby.ctl /opt/oracle/app/oradata/orcl/control01.ctl
# cp /data/oracle_data/dataguard/standby.ctl /opt/oracle/app/flash_recovery_areduplicate target database for standby from active database nofilenamecheck;a/orcl/control02.ctl

4.2.11 设置备库应用

复制成功后,备库自动被加载为mount模式,进入sqlplus查看

-- 查看备库实例状态
SQL> select status from v$instance;
-- 开启备库只读(oracle 11g:adg) alter database open read only
SQL> alter database open;
-- 后台实时日志应用
SQL> alter database recover managed standby database using current logfile disconnect from session;


-- 从库也可以直接startup,会默认mount为从库和open为只读

4.2.12 查看主备库状态

-- 查看主备库状态
SQL> select database_role,protection_mode,protection_level,open_mode from v$database; 

-- 查看dg日志
SQL> select * from v$dataguard_status;
select * from (select a.*,rownum rn from v$dataguard_status a order by timestamp desc) where rn=1;
-- 查看dg:process
SQL> select process,client_process,sequence#,status from v$managed_standby; 

4.3 测试

4.3.1 日志同步测试(DG功能)

主库执行:

SQL> archive log list;
SQL> alter system switch logfile;
SQL> archive log list;

备库查看:

SQL> archive log list;

4.3.2 DDL&DML 同步测试

主库执行:

-- 表空间
SQL> create tablespace testtablespace datafile '/home/oracle/ora_dbf/testkts.dbf' size 2m autoextend off;
-- 用户
SQL> create user testuser identified by 1 default tablespace testtablespace;
SQL> grant resource,connect to testuser;
-- 表
SQL> create table testuser.testtable ( id number(30), name varchar2(20) );
-- 索引
SQL> create index testindex on testuser.testtable(id);
-- 数据
SQL> insert into testuser.testtable(id,name) values(1,'first record'); 
SQL> commit;
-- 切换日志
SQL> alter system switch logfile;

-- 查看备库
SQL> select * from testuser.testtable;

	ID NAME
---------- --------------------
	 1 first record

测试记录

主要做dg的正常及异常测试,然后关注主库的发送日志(备库接收)和备库的应用日志

测试案例:

  • 正常情况
  • 异常情况
    • 备库tns异常
    • 备库数据库关闭
    • 备库网络异常未接收/应用
    • 备库主机宕机
    • 备库日志被清理
    • 主库tns异常
    • 主库数据库关闭
    • 主库网络异常未发送
    • 主库主机宕机
    • 主库日志被清理
  • 其他特性
    • 日志压缩
    • 保护模式

1. 正常情况

-- 表空间
SQL> create tablespace testtablespace datafile '/home/oracle/ora_dbf/testkts.dbf' size 2m autoextend off;
-- 用户
SQL> create user testuser identified by 1 default tablespace testtablespace;
SQL> grant resource,connect to testuser;
-- 表
SQL> create table testuser.testtable ( id number(30), name varchar2(20) );
-- 索引
SQL> create index testindex on testuser.testtable(id);
-- 数据
SQL> insert into testuser.testtable(id,name) values(1,'first record'); 
SQL> commit;
-- 切换日志
SQL> alter system switch logfile;

-- 查看备库
SQL> select * from testuser.testtable;

	ID NAME
---------- --------------------
	 1 first record

2. 异常情况

2.1 备库tns异常

结论:备库监听停掉对同步数据无影响。但是备库无法被应用连接

操作:

  • 关闭备库tns
  • 主库做dml,ddl操作
  • 查看备库

分析:主库发送日志通过参数log_archive_dest_2设置service=standby,这个standby应该是主库配置的tns里的。但是备库的tns挂掉没影响主库发送数据,同样不影响备库接收和应用数据。

这里在用tnsping standby 是报错无监听的,但是还是发过去了,可能是有缓存?

重启了主库,备库没有正常同步和应用。查看v$dataguard_status,连接standby出错了,应该还是有缓存。先这样吧…不去追究缓存多久啥啥啥的了==

重启备库监听,重启主库,备库自动同步ok

-- 初始状态
-- 主库
SQL> select * from testuser.testtable;

	ID NAME
---------- --------------------
	 1 first record
	 1 first record
	 1 first record

-- 备库
SQL> select * from testuser.testtable;

	ID NAME
---------- --------------------
	 1 first record
	 1 first record
	 1 first record
# 备库停止监听
[oracle@babc3aa59656 ~]$ lsnrctl stop

LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 12-MAY-2020 10:44:23

Copyright (c) 1991, 2009, Oracle.  All rights reserved.

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=172.17.0.3)(PORT=1521)))
The command completed successfully

# 主库查看监听
[oracle@c368a7303eee ~]$ tnsping standby

TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 12-MAY-2020 10:44:36

Copyright (c) 1997, 2009, Oracle.  All rights reserved.

Used parameter files:


Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.3)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orcl)))
TNS-12541: TNS:no listener
-- 主库操作dml
SQL> drop table testuser.testtable;

Table dropped.

-- 备库查询
SQL> select * from testuser.testtable;
select * from testuser.testtable
                       *
ERROR at line 1:
ORA-00942: table or view does not exist

2.2 备库数据库关闭

结论:主库日志正常保存的情况下,备库数据库关闭/宕机/网络异常等不影响。备库正常时,会比较两边的日志,具有一定的自动修复gap能力。(需要配置fal_server

操作:

  • 关闭备库
  • 主库操作dml,ddl
  • 查看日志情况

分析:数据库会记录已归档日志,主备库会通过类似比较日志记录号来确认日志是否备份OK,对于备库日志确缺失的时候(归档日志或实时日志),在设置fal_server参数情况下,备库具有一定自动修复gap的能力,会从主库下载缺少的日志文件并应用。

-- 关闭备库
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.

-- 主库dml操作,并切换日志
SQL> insert into testuser.testtable(id,name) values(1,'first record');

1 row created.

SQL> commit;

Commit complete.

SQL> alter system switch logfile;

System altered.


-- 切换日志,主库归档一条,备库日志文件夹没变化,查看主库dataguard_status,可以看到主库往备库standby写日志写挂了

-- 重启备库
SQL> startup
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size		    2214936 bytes
Variable Size		  251659240 bytes
Database Buffers	  260046848 bytes
Redo Buffers		    8015872 bytes
Database mounted.
Database opened.

-- 备份数据
SQL>  alter database recover managed standby database using current logfile disconnect from session;

Database altered.

SQL> select * from testuser.testtable;

	ID NAME
---------- --------------------
	 1 first record
	 1 first record
	 1 first record

SQL> 


-- 这里看到备库从主库下载了中间差的几条日志,说明dg是有自动修复gap的能力的,会从主库下载有差异的日志文件。
-- 这里比较显然的如果主库的归档文件丢了,就自动修复不了了,需要人工处理,后面看看怎么人工处理?归档文件都丢了...
-- 用rman增量更新应该是可行的?

2.3 备库网络异常未接收/应用

同[2.2 备库数据库关闭](#2.2 备库数据库关闭),主库正常,备库有一定的自动修复能力。

2.4 备库主机宕机

同[2.2 备库数据库关闭](#2.2 备库数据库关闭),主库正常,备库有一定的自动修复能力。

2.5 备库日志被清理

同[2.2 备库数据库关闭](#2.2 备库数据库关闭),主库正常,备库有一定的自动修复能力。

这里需要注意的是,日志=归档日志+实时日志。对于归档日志可以通过archive log list 查看已归档日志的序号,具体的归档日志文件dbf我们姑且认为记录了这种序号(大概是scn什么的…),所以增量归档是在已归档基础上继续备份新的文件并重置序号。通常也是比较archive log对比主备库是否一致。

所以删除备库之前的文件也没啥用因为已经归档过了,删除之后的文件也就和宕机一直没下载一样,如果是没应用的日志,还是会自动修复gap重新从主库下载。

2.6 主库tns异常

首先应该明确都是tns挂了=应用连接数据库挂了。这时候其实主库正常来说也是不会再产生有效的应用日志了。

问题不是很大,修复正常应该就可以了。

备库tns的时候已经测试了,数据库启动时应该是缓存了有效的tns的,所以单纯的tns挂掉不影响什么

2.7 主库数据库关闭

个人认为这时候通常更多可能是先看看主库是不是能快速正常启动,实在over的情况会切换到备库。

这里的高可用首选应该还是RAC,要么真的是机房宕机,停电等,切换第二机房备库。

主备库切换详见后文

2.8 主库网络异常未发送

这里应该认为是发送到备库网络异常

如果主库网络本身异常,通常来说和主库数据库关闭、宕机类似的,应用连接会异常。可以认为主库已经挂掉了。这时候是考虑修复主库或者切换备库吧

发送到备库异常同[2.3 备库网络异常未接收/应用](#2.3 备库网络异常未接收/应用),也没啥影响

2.9 主库主机宕机

参见 [2.7 主库数据库关闭](#2.7 主库数据库关闭)

2.10 主库日志被清理

这个还是比较麻烦的,描述的是备库日志没有而且主库日志也缺失,正常应该也不会出现这种情况:

  1. 主库会开启lgwr,进行实时日志发送(归档日志也发送)
  2. 备库可以被主库设置延迟应用也可以自己设置实时应用,除非挂掉了也不会出现日志缺失的情况
  3. 就算归档日志清理,通常也会保留大量的最近日期文件,不会直接全清了或者清错了

其实考虑这个就是基于日志清理考虑的,日志是占硬盘的,不可能一直存着。

如果实实在在出现这个情况,只能手工处理,在后面异常情况描述。

3. 其他特性

日志压缩当然还是能开启就开启的好。

保护模式可设置lgwr async, 尽量实时发送日志,async保证一定性能

3.1 日志压缩

这个是11g的新特性,说是adg的部分,要花钱的,配置方式如下:(LOG_ARCHIVE_DEST_N增加COMPRESSION=ENABLE

LOG_ARCHIVE_DEST_2='SERVICE=CHICAGO ASYNC NOAFFIRM VALID_FOR=(ONLINE_LOGFILE, PRIMARY_ROLE) REOPEN=60 COMPRESSION=ENABLE  DB_UNIQUE_NAME=CHICAGO'

这个大概要用一些网络测试工具才能搞出来,傻傻的比较了下日志文件,然后发现大小一样,暂时先放弃了

查了下,有一些dba也有一些测试文档,资料不是很多,我们就科普一下。

另外我觉得这个如果是实时日志的话可能会导致整体较慢,但是单次性能看不出效果来,实时日志数据量太小了。发送归档文件大概能看出来。整体较慢感觉更体现在一直占用网络和io吧

归档文件的疑惑:备库的standby日志组和主库的standby组是一样的,如果备库的standby组一个在写的日志文件快满的时候,主库挂了,这时候备库的standby redo log怎么办?这里应该和主库处理一样,那主库怎么处理?嗯…都记录了日志只是没有归档,应该问题不大,主库的standby redo log 和 备库的standby redo log一直保持一样就可以了

3.2 保护模式

我们已经知道dg有3中保护模式,最高可用、最大可用都要求日志必须写到备库。

这里想了解的是:

  1. 设置保护模式会影响参数设置吗?
  2. 参数设置会修改保护模式吗?

为什么要了解这个,因为最高可用和最大可用都要求要写到备库,从日志参数来说必须lgwr sync affirm,但这个日志参数不确定是哪种保护模式。

测了一下,日志LOG_ARCHIVE_DEST_n会影响保护模式,两者相互制约的。强行设了最大保护,参数用arch,数据库起不来,查看alert日志报错如下:

LGWR: Primary database is in MAXIMUM PROTECTION mode
LGWR: Destination LOG_ARCHIVE_DEST_2 is not serviced by LGWR

另外查询资料说,数据库可以在open模式下从最大性能先升级到最高可用再升级到最大保护,不能直接升级。是使用默认的参数可以这样?目测如果一样设置了比如lgwr async 可能启动最高可用也是有问题的

-- 修改参数
-- *.log_archive_dest_2='service=standby arch async valid_for=(online_logfiles,primary_role) compression=enable db_unique_name=standby' 
-- 创建spfile
-- SQL> create spfile from pfile='/data/oracle_data/dataguard/initora.ora';

-- 主库设置最大保护
-- shutdown
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
-- startup mount
SQL> startup mount;
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size		    2214936 bytes
Variable Size		  251659240 bytes
Database Buffers	  260046848 bytes
Redo Buffers		    8015872 bytes
Database mounted.
-- alter
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE PROTECTION;  
ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE PROTECTION

Database altered.

-- open
SQL> alter database open; -- 这里应该open不起来了或者上面alter就alter不了,我不是按这个步骤来的结果是一样的

手工操作

1. 主备切换

主备库的切换不复杂,但应该结合实际情况,考虑:

  1. 是否需要切换
  2. 是否临时切换
  3. 临时切换换回后增量日志同步

切换也应该考虑开始设置的主库和备库的参数是否合适,比如路径,服务等等(开始的参数其实就是全量参数,包含primary和standby的设置)

----查看主库状态
SQL> select OPEN_MODE,PROTECTION_MODE,PROTECTION_LEVEL,SWITCHOVER_STATUS from v$database;

OPEN_MODE	     PROTECTION_MODE	  PROTECTION_LEVEL
-------------------- -------------------- --------------------
SWITCHOVER_STATUS
--------------------
READ WRITE	     MAXIMUM PERFORMANCE  MAXIMUM PERFORMANCE
TO STANDBY

----进行切换
SQL> alter database commit to switchover to physical standby;

Database altered.
SQL> shutdown immediate;
ORA-01507: database not mounted


ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size		    2214936 bytes
Variable Size		  251659240 bytes
Database Buffers	  260046848 bytes
Redo Buffers		    8015872 bytes
Database mounted.
Database opened.
SQL> alter database recover managed standby database using current logfile disconnect from session;

Database altered.

-- 查看备库状态
SQL> select OPEN_MODE,PROTECTION_MODE,PROTECTION_LEVEL,SWITCHOVER_STATUS from v$database;

OPEN_MODE	     PROTECTION_MODE	  PROTECTION_LEVEL
-------------------- -------------------- --------------------
SWITCHOVER_STATUS
--------------------
READ ONLY	     MAXIMUM PERFORMANCE  MAXIMUM PERFORMANCE
TO PRIMARY

SQL> alter database commit to switchover to primary;

Database altered.

SQL> shutdown immediate;
ORA-01109: database not open


Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size		    2214936 bytes
Variable Size		  251659240 bytes
Database Buffers	  260046848 bytes
Redo Buffers		    8015872 bytes
Database mounted.
Database opened.

2. 备库宕机

宕机就宕机吧…问题不大

见 [2.2 备库数据库关闭](2.2 备库数据库关闭)

3. 主库宕机

先看看需不需要切换备库,切换备库如下

-- 前提:关闭主库
alter database recover managed  standby database finish;

-- 备库切换主库
SQL> alter database recover managed  standby database finish;

Database altered.

SQL> alter database commit to switchover to primary;

Database altered.

SQL> shutdown immediate;
ORA-01109: database not open


Database dismounted.
ORACLE instance shut down.
SQL> startup;
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size		    2214936 bytes
Variable Size		  251659240 bytes
Database Buffers	  260046848 bytes
Redo Buffers		    8015872 bytes
Database mounted.
Database opened.

-- 主库重启(状态出问题了,也做不了从库)
SQL> startup
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size		    2214936 bytes
Variable Size		  251659240 bytes
Database Buffers	  260046848 bytes
Redo Buffers		    8015872 bytes
Database mounted.
Database opened.
SQL> select OPEN_MODE,PROTECTION_MODE,PROTECTION_LEVEL,SWITCHOVER_STATUS from v$database;

OPEN_MODE	     PROTECTION_MODE	  PROTECTION_LEVEL
-------------------- -------------------- --------------------
SWITCHOVER_STATUS
--------------------
READ WRITE	     MAXIMUM PERFORMANCE  MAXIMUM PERFORMANCE
FAILED DESTINATION


SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> alter database commit to switchover to physical standby;
alter database commit to switchover to physical standby
*
ERROR at line 1:
ORA-01034: ORACLE not available
Process ID: 589
Session ID: 1144 Serial number: 5


SQL> startup mount standby database;
SP2-0714: invalid combination of STARTUP options
SQL> startup nomount;
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size		    2214936 bytes
Variable Size		  251659240 bytes
Database Buffers	  260046848 bytes
Redo Buffers		    8015872 bytes
SQL> alter database mount standby database;
alter database mount standby database
*
ERROR at line 1:
ORA-01665: control file is not a standby control file

-- 这里做不了从库了,重新复制standby controlfile过来替换就能做备库,暂时没看到其他的方法

4. 手工修复gap

gap:就是主库和备库差异的地方

之前说了主库日志完整的情况下, 备库是可以自动修复gap的。只能手工的话,基本就是主库日志也GG了。

参考:Dataguard gap修复

要通过SCM的增量备份修复GAP

5 其他参考

Oracle Data Guard 的配置方法,请参考下面的文档:

tip:链接过去可以看看很多oracle的特性和文档…

但是我们也不是oracle的dba啊,假装知道就好了

关于Active Data Guard的最佳实践经验,请参考文档:

Data Guard 日志传输参数:

dataguard 日志传输服务(参数解析)



  1. 12g版本及以前,lgwr是单进程;12c以后可以多进程 ↩︎

  2. Question: 这里主库日志切换是redo日志组满了,备库那边的日志组是什么情况,和主库一样? ↩︎

  3. Question: 这里怎么设置的?和最大保护如何区别,如果不设置超时时间,怎么判定备库不可用,而此时主库如何做?设置超时时间也是类似,超时是shutdown还是commit? ↩︎

  4. Question:这里事务提交会确认本地日志写成功吗?应该是的,否则就很容易出问题 ↩︎

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐