早些年有次生产库所在的主机主板故障,业务全停。我们没有备库,只能从最近的 RMAN 备份恢复——恢复本身花了四十多分钟,而那四十分钟里的交易全没了,因为备份是前一天半夜的。后来补上 Data Guard,再遇到主机维护或者硬件抽风,就是一条命令把角色切到备库,业务几乎无感。这件事让我觉得,单库跑得再稳,也算不上「高可用」,真正的保险是旁边还躺着一份实时跟得上的副本。
RMAN 备份恢复我另写过专文,日常运维里那些 ORA 报错也写过,这篇只讲 Data Guard(后面简称 DG)——怎么从零建一套物理备库,怎么用 broker 管理,以及切换时那些会咬人的细节。
DG 是个什么东西
简单说,DG 是在主库(primary)之外放一个或多个备库(standby),主库产生的 redo 日志实时传到备库,备库再把 redo 应用(apply)到自己的数据文件上,从而保持和主库一致。
备库分两种:物理备库(physical standby)是按块级别复刻,和主库一模一样,靠 Redo Apply(MRP 进程)回放;逻辑备库(logical standby)是把 redo 转成 SQL 再执行,备库可以是不一样的 schema,用来做报表分流之类。绝大多数容灾场景用物理备库就够了,这篇也只讲物理备库。
redo 怎么传、怎么应用,决定了你能丢多少数据:
- MAXIMUM PROTECTION(最大保护):redo 必须同步写到备库才提交,备库挂了主库也跟着停。零数据丢失,但牺牲可用性,一般只在同机房、网络极稳时才敢用。
- MAXIMUM AVAILABILITY(最大可用):正常情况下同步写,备库不可用时自动降级成异步,不阻塞主库。是保护性和可用性的折中,生产常用。
- MAXIMUM PERFORMANCE(最大性能):异步传 redo,主库不等备库,性能最好,极端故障可能丢一点已提交事务。跨机房 DR 基本都选它。
我们线上是同城主备用异步(性能模式),redo 传过去通常也就毫秒到秒级延迟,够用了。
动手前先把前置条件捋清楚
别急着敲命令,下面这些不满足,后面全是坑:
- 主备 Oracle 大版本一致,都是 19c(小版本尽量也对齐,避免 apply 阶段出问题)。
- 主库必须开归档:
SELECT log_mode FROM v$database;得是 ARCHIVELOG,否则 DG 无从谈起。 - 强制日志:
ALTER DATABASE FORCE LOGGING;,否则某些 nologging 操作不会进 redo,备库就对不上了。 - db_name 相同,db_unique_name 不同:主备是同一个「数据库家族」,但各自要有唯一名,redo 传输靠 unique_name 寻址。
- 两端都要有 Standby Redo Log(SRL):这是最常被漏掉的一项。SRL 用于在备库接收主库 redo,大小和组数要匹配主库 online redo。没它,apply 会卡。
- 网络通、监听通、密码文件一致:主备 SYS 密码必须相同,否则 redo 传输连不上(ORA-16191)。
目录结构尽量一致最省心;如果不一致,靠 db_file_name_convert / log_file_name_convert 参数做路径映射。
主库先改参数
-- 强制日志(如果还没开)
ALTER DATABASE FORCE LOGGING;
-- 配置 DG 配置名,两端都要能解析到对方
ALTER SYSTEM SET LOG_ARCHIVE_CONFIG='DG_CONFIG=(orcl_pri,orcl_stb)' SCOPE=BOTH;
-- 2 号归档目的地指向备库,ASYNC 异步,VALID_FOR 指定角色
ALTER SYSTEM SET
LOG_ARCHIVE_DEST_2='SERVICE=orcl_stb LGWR ASYNC
VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE)
DB_UNIQUE_NAME=orcl_stb' SCOPE=BOTH;
ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_2=ENABLE SCOPE=BOTH;
-- 备库角色时的回连配置(提前设,切换后直接生效)
ALTER SYSTEM SET FAL_SERVER=orcl_stb SCOPE=BOTH;
ALTER SYSTEM SET STANDBY_FILE_MANAGEMENT=AUTO SCOPE=BOTH;
-- 切几次日志,让配置生效、也顺手验证归档能生成
ALTER SYSTEM SWITCH LOGFILE;
tnsnames.ora 里要能解析 orcl_pri 和 orcl_stb 两个连接串,监听也要配好。这里有个坑:备库在 mount 状态还没 open,动态注册不上监听,所以备库这边的 listener.ora 必须配静态注册,否则 RMAN 复制时连不上 auxiliary:
# 备库 listener.ora 片段
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = orcl_stb)
(ORACLE_HOME = /u01/app/oracle/product/19.0.0/dbhome_1)
(SID_NAME = orcl)))
备库参数文件与密码文件
先把主库密码文件原样拷到备库,保证 SYS 密码一致:
# 在主库所在机器,scp 到备库
scp $ORACLE_HOME/dbs/orapworcl standby:/u01/app/oracle/product/19.0.0/dbhome_1/dbs/orapworcl
从主库导出 pfile,改出备库版:
-- 主库执行
CREATE PFILE='/tmp/initpri.ora' FROM SPFILE;
拷到备库后,至少改这几项:
*.db_unique_name='orcl_stb'
*.control_files='/u01/app/oracle/oradata/orcl/control01.ctl'
*.log_archive_dest_1='LOCATION=/u01/app/oracle/arch VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=orcl_stb'
*.log_archive_dest_2='SERVICE=orcl_pri ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=orcl_pri'
*.fal_server='orcl_pri'
*.fal_client='orcl_stb'
*.standby_file_management='AUTO'
*.db_file_name_convert='/u01/app/oracle/oradata/orcl/','/u01/app/oracle/oradata/orcl/'
*.log_file_name_convert='/u01/app/oracle/oradata/orcl/','/u01/app/oracle/oradata/orcl/'
路径一致的话 convert 可以写自己映射到自己;不一致就填真实的主/备路径对。
用 RMAN 复制建库,不用先备份
19c 最舒服的一点是 DUPLICATE ... FROM ACTIVE DATABASE,直接从运行中的主库复制,不需要提前做备份:
# 备库先用临时 pfile 起到 nomount
sqlplus / as sysdba <<EOF
STARTUP NOMOUNT PFILE='/tmp/initstb.ora';
EOF
# RMAN 同时连主库(target)和备库(auxiliary)
rman TARGET sys/@orcl_pri AUXILIARY sys/@orcl_stb <<EOF
DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE
DORECOVER
NOFILENAMECHECK;
EOF
DORECOVER 让复制完顺带追平到当前 SCN,NOFILENAMECHECK 在路径一致时跳过检查。如果路径不同,把 convert 配对,去掉 NOFILENAMECHECK。这一步跑完,备库控制文件、数据文件就都就位了。
复制成功后,启动日志应用(MRP):
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
DISCONNECT FROM SESSION 让它后台跑,不占用当前会话。
建完必须验,别凭感觉
-- 备库角色与状态,应为 PHYSICAL STANDBY / MOUNTED
SELECT name, open_mode, database_role FROM v$database;
-- MRP、RFS 进程是否起来
SELECT process, status, thread#, sequence#, block# FROM v$managed_standby;
-- 看归档有没有被应用
SELECT sequence#, applied FROM v$archived_log
WHERE applied='YES' ORDER BY sequence# DESC FETCH FIRST 10 ROWS ONLY;
-- 有没有缺口
SELECT * FROM v$archive_gap;
-- 传输延迟和应用延迟
SELECT name, value, time_computed FROM v$dataguard_stats
WHERE name IN ('transport lag','apply lag');
v$managed_standby 里能看到 MRP0(管理恢复进程)和 RFS(接收 redo)都在跑,才算链路通了。apply lag 一开始可能是几分钟(追初始量),稳定后应回落到秒级。
上 DG Broker,切换变成一条命令
手动管 redo 目的地和角色切换容易出错,Oracle 自带的 DG Broker 能把这些收拢成一个配置,还能自动监控。
-- 主备都开
ALTER SYSTEM SET dg_broker_start=true SCOPE=BOTH;
dgmgrl sys/@orcl_pri <<EOF
CREATE CONFIGURATION dg_config AS
PRIMARY DATABASE IS orcl_pri
CONNECT IDENTIFIER IS orcl_pri;
ADD DATABASE orcl_stb AS
CONNECT IDENTIFIER IS orcl_stb
MAINTAINED AS PHYSICAL;
ENABLE CONFIGURATION;
EOF
# 看状态
dgmgrl sys/@orcl_pri "SHOW CONFIGURATION"
dgmgrl sys/@orcl_pri "SHOW DATABASE orcl_stb"
broker 会自己管 LOG_ARCHIVE_DEST_2 和角色转换,比手写参数稳。配置里 StatusReport 显示 SUCCESS 才算健康。
切换:计划内和突发
Switchover(计划内,比如主机检修)——零数据丢失,主备角色互换,两边都还能继续用:
dgmgrl sys/@orcl_pri "SWITCHOVER TO orcl_stb"
执行完,原来的备库变主库并 open,原来的主库自动变成备库并 mount + 起 MRP。整个过程业务只断几秒连接。我经常拿它做容灾演练,半年切一次,既验证 DG 健康,也顺便确认应用连新主库没问题。
Failover(主库真挂了,突发):
dgmgrl sys/@orcl_stb "FAILOVER TO orcl_stb"
failover 之后,原主库就「废」了——除非它在挂之前开了闪回(flashback),否则不能简单 reinstate,得重建。所以有两个实践要点:一是给主库开 DB_FLASHBACK_ON 留出 reinstate 的余地;二是 failover 这种事,平时多演练,别等真出事才第一次敲命令。
broker 里 reinstate 旧主库的命令是 REINSTATE DATABASE orcl_pri,前提是闪回开着、且旧主库还能起来。
那些必踩的坑
- 漏建 SRL:备库 apply 卡住,MRP 一直
WAIT_FOR_LOG,alert 里循环报缺日志。组数和大小对齐主库 online redo,一般多一组。 - 密码文件不一致:RFS 连不上主库,报 ORA-16191。拷贝主库 orapw 是最稳的,别在备库单独设不同密码。
- 备库没配静态监听:RMAN duplicate 直接报 auxiliary 没注册,连不上。记住 mount 状态的库不会动态注册。
- convert 参数写反或漏写:数据文件落到奇怪路径,MRP 报找不到文件。复制前先在 pfile 里核对主备路径对。
- 没开归档就建 DG:redo 没地方传,架构不成立。先
ARCHIVELOG再谈 DG。 - 归档缺口且缺备份:
v$archive_gap报缺某段,而那段归档在主库也没了,就只能从 SCN 做增量备份滚备库,麻烦得多。所以 DG 之外,归档的保留和备份照样不能省。 - 网络抖动:redo 传输挂起,看
SELECT dest_id, status, error FROM v$archive_dest_status;定位,错误码会告诉你是对端监听没起还是密码不对。
最后一句大实话
DG 解决的是「主机、机房、站点挂了」这类故障,但它不是备份。你在主库上 DROP TABLE 删错表,这个删除会顺着 redo 秒级同步到备库,备库一样没这张表。真正的数据安全是 DG(抗硬件/站点故障)加 RMAN 备份(抗逻辑错误、保留历史)两条腿一起走。另外,别把 switchover 当成一年一次的稀罕事,定期演练才能让它在真出事时靠得住——你不会希望第一次执行 FAILOVER 是在凌晨三点、业务全停、而你还不确定命令敲得对不对的时候。

