Oracle Data Guard物理备库搭建:RMAN备份恢复踩坑与排查全记录
上个月给某套核心业务库搭建Data Guard考虑到生产环境不能停我选了“备份恢复”这条最稳妥的路线。原以为就是常规的RMAN备份、传到备机、恢复控制文件、起MRP结果一晚上接连踩了路径不映射、参数没对齐、归档链路断档、密码文件不一致好几个坑前前后后折腾到后半夜才把主备同步拉到正常。这篇就把这次用备份恢复创建Data Guard的问题记录整理成文每一步报错、定位过程、最终解法都按当时现场的逻辑梳理出来。如果你也正准备用RMAN备份方式给生产库搭物理备库或者搭完之后MRP一直不干活、v$archive_gap冒出来都查不出来这篇应该能帮你少走不少弯路。1. 为什么选备份恢复这条线适用场景与整体链路1.1 备份恢复 vs duplicate 怎么选创建Data Guard方案时在线的传统做法是直接用RMAN duplicate它支持FROM ACTIVE DATABASE不需要停库从主库在线拷贝数据文件到备库一步到位。遇到网络带宽充足、主库压力可控的场景duplicate确实香尤其11g以后duplicate会自动处理很多文件路径和状态问题。可我在现场不太敢一上来就赌duplicate原因有三个一是主库数据量不小在线复制过程中主库日志切换和生产IO会有明显叠加二是duplicate出错时排查链路长特别是RMAN自动生成的恢复脚本黑盒感强有些日志只有到报错时才能看到三是这套系统的重要程度决定了我必须用最“笨”但最可控的方式——RMAN全量备份加归档备份先把生产库的快照完整落到备机再手工控制恢复节奏。备份恢复这条线的核心思路是主库在备份窗口内做全量备份连同备份后的归档日志一起送到备机备机上恢复参数文件、控制文件再restore数据文件并应用归档到一致状态最后配置DG参数和日志传输链路。整个过程主库正常对外服务备份期间的新变化全部由归档承载。1.2 全链路步骤和前置条件实际操作顺序大概是主库开FORCE LOGGING避免后续有NOLOGGING操作造成备库数据空洞。确认归档模式开启归档目录有足够空间。RMAN备份全量数据库加归档日志备份尽量在业务低峰执行。备机安装相同版本数据库软件建好目录准备参数文件。将备份集传送到备机恢复SPFILE、控制文件用NO MOUNT方式设置备库参数。在备库做RMAN RESTORE DATABASE恢复数据文件。配置监听、tnsnames、密码文件。备库创建STANDBY LOGFILE启动MRP进程。验证归档传输和应用延迟。前置条件里最容易遗漏的是密码文件。DG主备之间的日志传输和FAL进程都要通过SYS用户建立远程会话备库密码文件里的SYS密码必须和主库一致。这个我在问题四里栽过一次后面专门讲。1.3 这条路线最容易出问题的三个环节我这次踩坑集中在三个环节。第一是文件路径映射主备库目录结构不一致时RMAN恢复出来的数据文件路径对不上备库MOUNT后一打开就报ORA-01157/01110这是新手第一个必踩的坑。第二是参数文件恢复后没有针对备库角色做修正很多人直接把主库SPFILE恢复到备库db_unique_name没改、FAL相关参数缺失结果备库虽然能起来MRP进程却一直不干活。第三是归档传输链路备份窗口和DG配置窗口之间产生的归档如果没送到备库MRP一启动就报GAP接着又可能因为监听、tnsnames、密码文件对不上导致FAL会话建不起来。这三个环节下面逐一还原。2. 备库起不来的第一道坎文件路径映射与目录规划2.1 报错现场ORA-01157/ORA-01110那天的第一波报错出现在备库RMAN恢复完数据文件、执行ALTER DATABASE OPEN的时候。备库日志里干干净净两行ORA-01157: cannot identify/lock data file 1 - see DBWR trace file ORA-01110: data file 1: /u01/app/oracle/oradata/PROD/system01.dbf这说的是系统表空间文件在备机上根本找不到。主库的数据文件放在/u01/app/oracle/oradata/PROD下而备机当时磁盘规划是/u02/app/oracle/oradata/STANDBY底层物理磁盘都在/u02上。RMAN恢复时不管你备库长什么样直接把备份里记录的路径原封不动restore出来。备机没有/u01这个挂载点DBWR进程锁文件自然锁不上。当时第一反应是查RMAN的SET NEWNAME有没有生效一查发现我根本没写。RMAN restore database在目标库控制文件里记录的路径不存在时最优解是用SET NEWNAME配合DB_FILE_NAME_CONVERT这类参数去做路径映射而不是手动猜。2.2 主备目录不一致时的转换原理DG体系里主备库路径可以不同但数据库要知道怎么把主库的文件路径翻译成备库的路径。翻译动作有两层一层交给RMAN在恢复阶段用SET NEWNAME指定备库实际目录另一层交给数据库本身通过DB_FILE_NAME_CONVERT和LOG_FILE_NAME_CONVERT两个参数在备库应用日志、添加数据文件、创建在线日志时自动完成路径替换。转换参数的格式就是成对的字符串替换DB_FILE_NAME_CONVERT主库路径,备库路径。数据库拿到主库传来的文件路径时把左边字符串替换成右边。需要注意它做的是前缀或全路径字符串替换不是目录拼接。比如主库路径含PROD备库想换成STANDBY就得写成DB_FILE_NAME_CONVERT/oradata/PROD,/oradata/STANDBY。日志文件同理LOG_FILE_NAME_CONVERT负责在线日志和standby日志的路径映射。顺带多说一句OMF环境下文件路径是Oracle自动管理的DB_CREATE_FILE_DEST这类参数决定文件落点路径转换场景相对少。但传统非OMF环境特别是老库几乎都靠这两个参数硬映射配错一个后面所有新加的数据文件都会掉链子。2.3 转换参数的配置与验证我的备库SPFILE是从主库备份恢复出来的路径相关参数还是主库的值所以恢复完成后第一件事是动态改掉ALTER SYSTEM SET DB_FILE_NAME_CONVERT/u01/app/oracle/oradata/PROD,/u02/app/oracle/oradata/STANDBY SCOPEBOTH; ALTER SYSTEM SET LOG_FILE_NAME_CONVERT/u01/app/oracle/oradata/PROD,/u02/app/oracle/oradata/STANDBY SCOPEBOTH;注意SCOPEBOTH因为数据库已经用SPFILE启动同时要写进SPFILE否则重启又丢。改完参数后RMAN端还要重新指定一遍恢复路径跟参数互相配合RUN { SET NEWNAME FOR DATABASE TO /u02/app/oracle/oradata/STANDBY/%U; RESTORE DATABASE; SWITCH DATAFILE ALL; }这段脚本里SET NEWNAME把每个数据文件统一放到/u02SWITCH DATAFILE ALL让控制文件指向新路径。没有SWITCH这一步控制文件记录的还是主库老路径后面照样报ORA-01157。验证就简单了查v$datafile看每行文件的NAME列是不是已经落在备库实际目录下。再查v$logfile确认在线日志路径也换过来了。当时我把v$datafile的输出扫了一遍确认没有一条指向/u01心才放下一半。2.4 备库目录规划建议经历过这次我对备库目录规划有了几个固定习惯备库文件系统布局最好一开始就和主库保持同一套命名风格即使挂载点不同也要保证db_file_name_convert写起来是一条简单的字符串替换。比如主库/u01/oradata备库/backup/oradata两个路径都包含oradata替换规则清晰不容易出错。另外数据文件、控制文件、在线日志、归档日志、standby log的目录要分开。归档目录和standby log目录如果跟数据文件挤在同一块盘备库压力上来时IO互相干扰特别是MRP应用和远程归档同时争抢容易让延迟越拉越大。我后来在备机上把归档目录独立挂了一块盘再把standby log放到单独目录应用延迟稳定了很多。3. MRP进程不干活参数文件、db_unique_name和FAL配置的连环坑3.1 现象MRP0停滞在not started文件路径问题解决后备库成功MOUNT并打开到READ ONLY状态我本以为马上就能起MRP。结果执行ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;语句正常返回但备库始终没有进入应用日志的状态。查v$managed_standbyMRP0进程根本不在列表里查v$dataguard_status只看到一条“MRP0 not started”之类的记录没有任何错误码提示。这比报错更烦人数据库层面几乎什么都没说只能从参数和日志两头反推。3.2 定位方法按日志逐层排查当时我把排查拆成三条线备库alert日志、主备两边的DG相关参数、连接链路可不可达。备库alert日志刷了几屏只看到RFS进程在接收文件但MRP始终没被触发。这时我意识到问题大概率出在参数配置上——MRP启动时会先校验备库角色和DG身份校验不过就静默放弃启动。先看两个最关键的参数SHOW PARAMETER DB_UNIQUE_NAME SHOW PARAMETER FAL_SERVER SHOW PARAMETER LOG_ARCHIVE_CONFIG一查就发现问题了db_unique_name还是主库的名字PROD而不是备库的STANDBY。FAL_SERVER和FAL_CLIENT压根没设置LOG_ARCHIVE_CONFIG也没有指定DG_CONFIG列表。等于备库完全不知道自己是备库也不知道该找谁要日志MRP自然无从启动。3.3 三处参数错误逐个击破第一处是db_unique_name。DG配置里主备库的db_unique_name必须不同主库是PROD备库就该是STANDBY。我当时是从主库备份恢复的SPFILE恢复后没改这个名字导致备库认为自己是主库角色数据保护配置全部指向本机。修改方法ALTER SYSTEM SET DB_UNIQUE_NAMESTANDBY SCOPESPFILE;改完必须重启实例才能生效因为db_unique_name属于静态参数。我记得当时为了快先SHUTDOWN IMMEDIATE再STARTUP MOUNT确认新名字生效后才继续。第二处是LOG_ARCHIVE_CONFIG。这个参数定义DG配置内允许传输日志的目标和来源DGID一定要两边写同一个名字。主备库我都设置成LOG_ARCHIVE_CONFIGDG_CONFIG(PROD,STANDBY)主库在PROD上备库在STANDBY上两边都识别同一个DG集合。如果这里忘了备库RFS收到主库日志时会因为DGID不匹配直接拒绝主库那边报ORA-16047措辞是DGID mismatch between destination and source database。第三处是FAL参数。FAL全称Fetch Archived Log备库发现归档有GAP时通过FAL_SERVER指定的源去拉缺失日志。备库上至少要配FAL_SERVER指向主库的tnsnames别名主库双向一般不会太严格但稳妥起见主备都写上FAL_SERVERPROD FAL_CLIENTSTANDBY这里的PROD和STANDBY是TNSNAMES里的别名不是db_unique_name。配完FAL还要顺手确认两件事主库LOG_ARCHIVE_DEST_2指向备库的目标设置正确备库本地有LOG_ARCHIVE_DEST_1保证自己归档路径可用。3.4 重启MRP的完整命令参数全部改完重启备库实例到MOUNT状态后我按顺序执行ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;这次v$managed_standby里终于出现了MRP0进程状态是WAIT_FOR_LOG意思是正在等主库传日志。备库状态也从READ ONLY变成READ ONLY WITH APPLY数据保护模式显示为MAXIMUM PERFORMANCE。之后从主库切换一次日志备库那边立即就能看到新归档被应用延迟从“未知”变成几秒这条线才算真正通了。4. 归档链路断档GAP问题与FAL会话建立失败的完整排查4.1 备份窗口期落下的归档日志MRP进程起来后我以为万事大吉结果过了半小时一看v$archived_log备库的归档序列号和主库差了二十几个。查v$archive_gap时果然出数据了THREAD# LOW_SEQUENCE# HIGH_SEQUENCE# 1 123 146这二十多个归档就是备份完成之后、DG参数配置完成之前这个窗口期在主库生成的日志。主库虽然从配置完成后开始实时传日志但历史缺口不会自动补需要一个触发机制把缺口拉过来。DG里这个活主要由FAL进程干备库发现自己有gap时向FAL_SERVER请求对应序列的归档。4.2 FAL无法连接备库tnsnames与静态监听的坑理论上FAL会自动把缺口拉平但我等了十分钟v$archive_gap纹丝不动。查备库alert日志看到连接主库的尝试反复失败错误是ORA-12541: TNS:no listener。第一次看到这个错我还以为备库监听没起再三确认监听是LISTENERONLINE。后来才反应过来主库要去连接备库的RFS进程但备库监听器配置的是动态注册而实例处于MOUNT状态下动态注册不会发生监听器上根本没有备库的服务名主库自然连不上。解决办法是给备库监听器加静态注册在listener.ora里手写SID_DESC指向备库实例名和Oracle路径SID_LIST_LISTENER_STANDBY (SID_LIST (SID_DESC (GLOBAL_DBNAME STANDBY_DGMGRL) (ORACLE_HOME /u02/app/oracle/product/11.2.0/dbhome_1) (SID_NAME STANDBY)))GLOBAL_DBNAME这里我写的是备库的db_unique_name加后缀主要用于DG Broker场景实际日志传输走的主要是SID_NAME。写完reload监听再测主库到备库的连接tnsping STANDBY sqlplus sys/sysSTANDBY as sysdba能登进去说明静态注册生效了。这一点经验后来成了我检查清单里的常驻项DG环境里备库只要处于MOUNT状态就别指望动态注册静态注册必须预先配好。4.3 密码文件不一致导致的ORA-01031监听通了之后我以为GAP马上能补上结果备库日志又开始刷ORA-01031: insufficient privileges。这个坑更隐蔽主库的SYS密码文件和备库的SYS密码不一致。主库之前因为安全加固改过SYS密码orapwd文件随之变化备库上的orapwSTANDBY文件还是当初建库时生成的里面记录的验证口令和主库完全对不上。DG日志传输无论走LGWR还是ARCH进程都要以SYS身份在两端建立远程会话口令不一致自然被拒。处理方式简单粗暴但有效在备机上用与主库相同的SYS密码重新生成密码文件。orapwd file$ORACLE_HOME/dbs/orapwSTANDBY password与主库一致的密码 entries10 forcey生成后重启监听再手动切换一次主库日志备库的ORA-01031立刻消失。这个坑提醒我两件事第一密码文件是DG链路的“握手凭据”任何一端改了SYS密码另一端必须同步改第二遇到ORA-01031别急着怀疑权限配置先看两端orapwd文件的修改时间和口令状态。4.4 手工补GAP后恢复同步FAL会话正常后理论上缺口会自动补齐但我习惯手工先补一把原因是备份窗口涉及大量归档等FAL一条条拉太慢。手工方式是把主库缺失序列对应的归档拷贝到备库然后在备库RMAN里catalogCATALOG START WITH /backup/archive/STANDBY/;REGISTER这些归档后MRP进程会自动应用。如果备库有多个线程还需要对每个THREAD#都检查gap。补完之后我再查v$archive_gap为空v$dataguard_stats里APPLY_LAG从几十分钟降到了0主备日志序列号完全对齐这一步才算彻底走完。5. 两个“平时不疼、切换要命”的隐患standby redo log与force logging5.1 standby redo log创建时机与大小设计归档链路补齐后主备同步已经正常但我在做切换预演前又发现两个隐患第一个是备库压根没建standby redo log。很多人以为备库只要接收并应用归档就行standby log可以后补——理论上确实能跑但遇到无主库实时传输或故障切换时它就是生死线。主库的日志先传到备库的standby redo logMRP才能实时应用没有standby log备库只能每来一个归档就应用一个切换瞬间很可能会丢日志。创建时机必须是MRP进程启动之前或至少是备库还在MOUNT且没有应用日志时。我当时把MRP先停掉然后按组创建ALTER DATABASE ADD STANDBY LOGFILE GROUP 10 (/u02/app/oracle/oradata/STANDBY/stdby10.log) SIZE 200M; ALTER DATABASE ADD STANDBY LOGFILE GROUP 11 (/u02/app/oracle/oradata/STANDBY/stdby11.log) SIZE 200M; ALTER DATABASE ADD STANDBY LOGFILE GROUP 12 (/u02/app/oracle/oradata/STANDBY/stdby12.log) SIZE 200M; ALTER DATABASE ADD STANDBY LOGFILE GROUP 13 (/u02/app/oracle/oradata/STANDBY/stdby13.log) SIZE 200M;组数原则是比主库在线日志组数多一组每组大小等于或略大于主库日志组大小。主库是3组200M我就建了4组200M保证备库在接收日志时有足够的缓冲组轮转不会因为日志同步速度跟不上而阻塞。5.2 force logging没开的代价第二个隐患是主库FORCE LOGGING为NO。这个问题平时不痛不痒但一旦主库有直接路径加载、NOLOGGING建索引之类的操作数据块的内容不会完整写入日志。备库通过归档应用时拿到的是数据块“空洞”要么MRP进程停滞报ORA-26040要么干脆把数据文件应用成不可用状态切换上去直接丢数据或者起不来。FORCE LOGGING的作用就是强制整个数据库所有操作都写日志不管语句里写了NOLOGGING还是UNRECOVERABLE统一按完整日志处理。我当时在主库执行SELECT FORCE_LOGGING FROM V$DATABASE;结果返回NO立刻补上ALTER DATABASE FORCE LOGGING;顺带说一句这个操作要在主库做而且要确认已经记录进数据字典。修改完再查FORCE_LOGGING变成YES。对于DG环境我建议搭建初期就开FORCE LOGGING而不是等业务跑起来发现有NOLOGING操作再回头补。5.3 隐患检查与补救操作这两个隐患的共同特点是不影响日常同步只影响故障切换。V$DATABASE里OPEN_MODE显示READ ONLY WITH APPLY、延迟为0不代表切换安全。我在切换预演前加了几条固定检查一查FORCE_LOGGING是否为YES二查standby logfile的组数和大小三查v$standby_log里有没有内容写入过。这些检查后来都进了我维护DG的标准清单。任何DG环境不管是不是我亲手搭的进入维护期第一件事就是过一遍这几个状态省得业务真的切换时被反杀。6. 把踩坑经验固化成检查清单6.1 数据库与进程状态检查这次搭建过程的每一步几乎都被某个坑卡了一下事后我把所有踩过的点整理成了一份巡检清单现在每次做DG巡检和切换预演都直接照做。第一步检查数据库角色和进程查v$database的DATABASE_ROLE备库必须是PHYSICAL STANDBY不能还是PRIMARY。查OPEN_MODE正常备库应该是READ ONLY WITH APPLY如果只是READ ONLY说明MRP没运行。查v$managed_standbyMRP0状态应为WAIT_FOR_LOG不能是NOT STARTEDRFS进程要存在数量要合理。查v$dataguard_statsAPPLY_LAG和TRANSPORT_LAG都应该接近0最好小于一分钟。查v$archive_gap结果为空才能说明日志链路没有缺口。这套命令我在备库上执行一遍只要几秒钟但能覆盖绝大部分“备库看起来正常、实际不干活”的场景。6.2 数据保护与日志链路检查第二步检查数据保护配置和日志链路。首先要确认FORCE_LOGGING是YES其次看LOG_ARCHIVE_DEST_2在主库上的配置是否指向备库参数里VALID_FOR(ONLINE_LOGFILES,PRIMARY_ROLE)这类条件是否写对。接着检查db_unique_name、FAL_SERVER、FAL_CLIENT、LOG_ARCHIVE_CONFIG两边是否完全对齐特别是DG_CONFIG里的库名跟db_unique_name要一致。再检查文件名转换参数是否覆盖了数据文件和日志文件两头。我之前就吃过只设了DB_FILE_NAME_CONVERT、没设LOG_FILE_NAME_CONVERT的亏虽然当时在线日志路径碰巧一样没暴露但一旦备库要自己创建新日志文件立刻就会路径错位。最后别忘了把standby logfile的组数和大小记下来确认它远大于主库在线日志组数避免切换瞬间日志缓冲不够用。6.3 收尾的一点个人体会这次最深的体会是DG搭建最麻烦的不是执行那些命令而是“静默失败”。路径不对、参数没改、密码不一致这些错误都不一定当时报错常常要等到MRP启动、GAP出现、切换演练才暴露。备份恢复创建DG又是一条很吃细节的路线链路上的每一环都有对应的“检查点”提前把检查清单跑一遍比启动MRP后慢慢排查高效得多。现在我去任何现场搭DG都用这套清单先自查一遍基本把当年那种半夜救火的节奏杜绝了。你如果正准备做同样的事情建议也先按照这个顺序把环境预检一遍再动RMAN恢复那一大串命令。