ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Oracle归档日志还原实战:从RMAN恢复到DG备库故障排查

Oracle归档日志还原实战:从RMAN恢复到DG备库故障排查 我一个DBA朋友前阵子凌晨三点给我打电话,说测试库被人误删了一张分区表,幸好开了归档模式,不然真是欲哭无泪。类似的场景在Oracle运维里太常见了——数据文件损坏、误DROP表、DG备库落后主库太多,这些情况基本都得靠归档日志来救命。今天就把我这些年做Oracle归档日志还原的实战经验完整梳理一遍,从原理到操作,从单机恢复到DG场景,再到各种奇葩坑,一次性讲透。这篇内容适合刚接触Oracle恢复的运维新手,也适合那些虽然会敲RMAN命令但遇到实际问题就发怵的进阶学习者。不管是11g还是19c,核心原理和操作思路完全通用,唯一的差别就是个别语法细节,我会在文中特别标注。1. 归档日志还原的本质:先把账本补齐,才能谈恢复很多人把还原和恢复混为一谈,这两个概念必须分开。还原(Restore)指的是把物理文件从备份集里拷贝回来,恢复(Recover)则是把归档日志和重做日志应用到数据文件上,让数据文件回到某个一致的时间点。归档日志在恢复里扮演的角色,就是记录从备份那一刻起所有数据变更的“账本”。1.1 数据库为什么必须要归档日志Oracle数据库默认是NOARCHIVELOG模式,这种模式下只保留当前联机重做日志,日志切换后就覆盖了。如果数据库崩溃,你只能恢复到最近一次备份时的状态,备份之后的数据全部丢失。生产库必须开归档模式,这已经是共识,没有讨论余地。开了归档模式后,每个联机重做日志切换时,Oracle的LGWR进程会把日志内容复制到归档日志目录。从备份点到故障点之间的所有数据变更,全部记录在这些归档日志里。恢复时,数据文件先回到备份状态,再通过应用归档日志重放所有变更,最终把数据库推进到故障前的那一刻。有个极容易混淆的细节:归档日志只记录重做记录,不直接存储数据块影像。恢复时Oracle会读取归档日志里的重做记录,找到对应数据块的变化,重新执行一遍这些操作。所以备份越新,需要应用的归档日志就越少,恢复也就越快。1.2 完整恢复与不完全恢复的分界线恢复操作分为两大类。完整恢复(Complete Recovery)是把数据库恢复到最新状态,所有已提交事务都不丢失,通常用于数据文件损坏、误删数据文件等场景。不完全恢复(Incomplete Recovery)则恢复到过去的某个时间点,常用于误DROP表、误TRUNCATE、批量更新错数据等场景。判断用哪种恢复,关键看两个问题一是数据文件是物理损坏还是逻辑损坏,二是你要回到哪个时间点。物理损坏做完整恢复,逻辑损坏(比如误删数据)做不完全恢复。2. 还原操作的完整链路:从确认备份到打开数据库这一节是全文核心,我会按实际操作顺序,把从确认备份有效性到最终打开数据库的每一步都拆开讲,包含所有需要执行的命令和为什么这么做的理由。2.1 事前准备工作:检查备份与归档连续性动手之前,必须先确认几件事,否则恢复做到一半发现备份不可用,那就真是叫天天不应了。首先是确认数据库处于什么状态,能否正常启动到mount阶段。然后是确认RMAN备份是否存在且有效,归档日志是否连续。# 检查数据库当前状态 sqlplus / as sysdba SQL select status from v$instance; SQL select log_mode from v$database; # 检查RMAN备份情况 rman target / RMAN list backup of database; RMAN list backup of archivelog all;这里有个很关键的检查项:确保归档日志没有断层。如果归档日志序列号出现跳号,比如1到10全在,但11没了,那恢复就只能停在第10个归档的位置,后面的数据全丢。这个检查最好在备份策略里定期做。我在实际运维中见过太多人忽略这一步,直接把备份文件拷贝到新机器上就开始恢复,结果恢复到最后报ORA-00283: recovery session canceled due to errors,一查才发现归档日志缺了好几个序列号,整个恢复进程白跑一遍。所以,动手前花5分钟做连续性检查,省得后面花几个小时补救。2.2 还原命令的准确书写方式:自动与手动两种路径RMAN提供了两种还原方式。一种是自动化程度极高的restore database加recover database,RMAN会自己读取备份集,决定要还原哪些数据文件,自动应用归档日志。另一种是手动指定文件、指定时间点,适用于精细控制的场景。# 自动方式:恢复到最新状态 RMAN startup mount; RMAN restore database; RMAN recover database; RMAN alter database open; # 不完全恢复:恢复到指定时间点 RMAN startup mount; RMAN restore database until time to_date(2024-01-15 14:30:00,yyyy-mm-dd hh24:mi:ss); RMAN recover database until time to_date(2024-01-15 14:30:00,yyyy-mm-dd hh24:mi:ss); RMAN alter database open resetlogs;注意alter database open resetlogs这一步。不完全恢复后,日志序列号会重置,之前的归档日志和重做日志都不能再用于这个库了,这是正常现象,不是错误。开库后第一件事就是做一次全库备份,因为resetlogs创建了一个新的数据库化身(incarnation),旧的备份和归档日志都不再适用于此后产生的数据。2.3 RESETLOGS与NOARCHIVELOG的坑不完全恢复必然要用resetlogs,这是Oracle的设计机制。问题在于,很多人resetlogs后不做全备,导致后续恢复时找不到与新化身匹配的备份,只能从头来过。恢复完成后立刻做全备,这是恢复操作的一部分,不是可选步骤。还有一种坑是有的环境里把数据库设成NOARCHIVELOG模式,然后指望用归档日志恢复,这根本不可能。NOARCHIVELOG模式下只有联机重做日志,日志切换就被覆盖了,既没有归档日志可应用,也不能做时间点恢复。如果生产库在NOARCHIVELOG模式,趁早改过来。3. 归档日志还原的高级玩法:不完全恢复与DG备库场景基础操作学会后,真正考验功力的是各种特殊场景。误删数据、备库Lag、主备切换失败,每一种都有不同的处理思路。3.1 误DROP表后的时间点恢复实操记录有一次开发同事误DROP了一张业务表,还好发现及时。我当时的处理思路是这样的:-- 第一步:确认误操作时间 -- 查v$session的logon_time,或者问开发大概时间点 -- 假设误操作发生在13:45-13:50之间 -- 第二步:恢复到一个临时时间点(比如13:40) RMAN startup mount; RMAN restore database until time to_date(2024-03-20 13:40:00,yyyy-mm-dd hh24:mi:ss); RMAN recover database until time to_date(2024-03-20 13:40:00,yyyy-mm-dd hh24:mi:ss); RMAN alter database open resetlogs; -- 第三步:把DROP的表导出 expdp system/xxx schemasapp_user directoryDATA_PUMP_DIR dumpfileapp_user.dmp logfileexpdp_app_user.log -- 第四步:把数据库闪回到正常状态 RMAN shutdown abort; RMAN startup mount; RMAN flashback database to time to_date(2024-03-20 13:59:00,yyyy-mm-dd hh24:mi:ss); RMAN alter database open resetlogs; -- 第五步:导入导出的表 impdp system/xxx directoryDATA_PUMP_DIR dumpfileapp_user.dmp logfileimpdp_app_user.log这套流程里,用到的是Oracle的闪回数据库特性(Flashback Database)。如果没开闪回,就只能用之前的全备再恢复一次,那就要多花几倍时间。闪回日志和归档日志是两个不同的东西,闪回日志记录的是数据块的前镜像,用于快速回退;归档日志记录的是重做信息,用于前滚。两者配合使用,就能做到恢复到过去某点导出数据,再回到现在导入数据。这里我要特别强调:不完全恢复的终点时间点选取,宁早勿晚。如果你不确定误操作是13:45还是13:47,就恢复到13:40。宁可丢失一点误操作前的正常数据,也不能把误操作本身恢复回来。恢复完之后导数据再确认,如果发现漏了,还可以再往前调整。3.2 DG备库落后主库的恢复策略Data Guard环境是归档日志应用的重灾区。备库因为网络抖动、磁盘空间不足、归档日志应用进程异常等原因,与主库产生较大GAP,这时候就需要手动补齐缺失的归档日志。# 在备库上查看GAP情况 sqlplus / as sysdba SQL select * from v$archive_gap; # 确认缺失的日志序列号后,从主库拷贝缺失归档日志到备库 # 假设缺失序列号1001-1010 # 在主库上执行 scp /u01/app/oracle/archive/1_1001_12345.arc standby_host:/u01/app/oracle/archive/ # 在备库上注册并应用 RMAN catalog archivelog /u01/app/oracle/archive/1_1001_12345.arc; RMAN catalog archivelog /u01/app/oracle/archive/1_1002_12345.arc; -- 重复注册缺失的所有归档日志 # 重启日志应用进程 SQL alter database recover managed standby database disconnect from session;如果备库落后主库实在太远,比如缺失几千个归档日志,直接补齐反而不现实,这时候更合适的做法是重新创建备库。判断标准其实很直接:看归档日志是否还在主库的归档目录里,如果在,补齐就行;如果已经被清理掉了,那只能重新创建备库。很多人纠结落后多久必须重建,其实核心不是时间,而是日志是否还在。补归档日志时,如果遇到备库应用进程一直报错,先查一下是不是归档日志放到了错误目录。我见过太多人把db_recovery_file_dest和自定义归档目录搞混,导致catalog archivelog找不到文件。用show parameter log_archive_dest确认两边配置一致,再往下排查。3.3 主备切换或还原后监听启动失败的真相热搜词里有oracle监听服务无法启动,这个在数据库还原场景里太常见了。数据库用RMAN还原到新机器后,监听起不来,十有八九是listener.ora或tnsnames.ora里的主机名、IP和实际环境对不上。还有一种是Oracle用户的环境变量没配好,导致lsnrctl找到的是错误目录下的配置。# 排查监听问题三板斧 # 1. 确认监听配置文件是否正确 cat $ORACLE_HOME/network/admin/listener.ora # 2. 确认监听状态 lsnrctl status # 3. 如果状态异常,尝试强制停止再启动 lsnrctl stop lsnrctl start # 如果端口被占用,查占用进程 netstat -tlnp | grep 1521还有一个隐藏较深的原因:数据库还原后,/etc/hosts里主机名被改了,但listener.ora还写着旧主机名。把两者统一后,监听基本就能正常起来。另外,DG环境里备库的log_archive_dest_state_2如果是defer,记得改成enable,否则备库永远不会接收主库传过来的归档日志,这也是GAP的一个隐藏来源。4. 归档日志还原的进阶排查:从报错到日志的全链路体检遇到还原失败,不要瞎猜,照着这条链路排查,大部分问题都能定位。4.1 从ORA报错到V$视图的定位逻辑先看报错,再看告警日志,最后查视图确认状态。这是一条标准排查链路。# 查看数据库告警日志,通常在这里 tail -200 $ORACLE_BASE/diag/rdbms/{dbname}/{instancename}/trace/alert_{instancename}.log # 查看当前归档日志生成情况 sqlplus / as sysdba SQL select sequence#, first_time, next_time from v$archived_log order by sequence#; SQL select * from v$recovery_progress;有一次恢复时报警ORA-00354: corrupt redo log block header,我第一反应是归档日志物理损坏。后来查了告警日志,发现其实是磁盘坏道导致的。换了一个健康的归档目录,把这块日志对应的数据文件重新还原,问题就解决了。注意,归档日志损坏不等于备份损坏,备份文件是独立的,归档日志坏了,重新从备份恢复,再应用后面的健康归档日志,是可以绕过坏日志的。前提是坏日志之后还有完整的日志链,或者你能接受坏日志这个时间点的数据丢失。4.2 闪回恢复区与日志空间耗尽生产环境里最常见的故障不是恢复本身,而是恢复前发现归档目录满了。Oracle的快速恢复区(Fast Recovery Area)默认有大小限制,归档日志写满后,数据库会直接挂起,这是很多人第一次遇到时完全想不到的。# 查看快速恢复区使用情况 SQL select name, space_limit, space_used from v$recovery_file_dest; SQL show parameter db_recovery_file_dest_size; # 紧急情况下,可以临时调大快速恢复区 SQL alter system set db_recovery_file_dest_size20G; # 清理过期归档日志(备份过的才能删除) RMAN delete archivelog all backed up 2 times to device type disk;清理归档日志要谨慎。如果归档日志还没做备份就删了,恢复时就会因为缺日志而失败。RMAN的delete archivelog all backed up 2 times意思是只删那些已经备份过2次的归档日志,这是安全写法。如果磁盘实在紧张,至少要保证最近几天的归档日志都完整保留,并且全备之后产生的归档日志不能删。针对归档目录空间,我强烈建议做监控脚本。如果空间使用率超过80%,就自动触发告警,脚本还能顺带查一下最近的归档日志连续性。# 每5分钟检查一次归档日志连续性 sqlplus -s / as sysdba EOF set pagesize 0 feedback off verify off heading off echo off SELECT GAP Found: || count(*) FROM (SELECT sequence# FROM v$archived_log WHERE sequence# (SELECT MAX(sequence#)-10 FROM v$archived_log)) WHERE sequence# NOT IN (SELECT sequence# FROM v$archived_log); exit; EOF4.3 还原速度慢与并行调优数据库越大,还原时间越长。如果恢复窗口很紧,必须用并行。RMAN的并行还原能显著节省时间,尤其是数据文件数量多的库。# RMAN并发配置 RMAN configure device type disk parallelism 4; RMAN configure channel 1 device type disk format /backup/%U; RMAN configure channel 2 device type disk format /backup/%U; RMAN configure channel 3 device type disk format /backup/%U; RMAN configure channel 4 device type disk format /backup/%U; # 恢复时显式指定并行 RMAN restore database; RMAN recover database;并行度设置多少合适?不是越多越好。通常按照CPU核数的一半来设置,如果服务器有16核,并行度设8,如果只有4核,设2。并行度过高会导致CPU争抢,反而拖慢整体速度。另外,恢复磁盘的IO能力也是瓶颈。用SSD和机械盘的恢复速度差距是数量级的。如果业务允许,临时把归档日志和重做日志放到SSD目录,恢复完成后再挪回来,是实战中常用的提速技巧。5. 还原方案设计:备份策略直接影响还原成功率很多人只在出事后才想起备份,这是最要不得的。备份策略设计得好不好,直接决定还原能不能成功。5.1 全量加增量加归档的三级火箭数据库备份不能只靠一种方式。我个人推荐周末全备周三增量每日归档备份的组合。全备提供基础数据文件,增量备份减少恢复时需要应用的归档日志数量,归档备份则保证日志链完整。三者配合,既能控制备份窗口,又能保证还原RPO(数据丢失容忍度)控制在分钟级。# 周末全备 RMAN backup database plus archivelog delete input; RMAN backup current controlfile; RMAN backup spfile; # 周三增量 RMAN backup incremental level 1 database plus archivelog delete input; # 每日归档备份 RMAN backup archivelog all delete input;delete input的意思是备份完成后删除已备份的归档日志,这是控制归档目录空间的有效方式。但要注意,如果这个命令因为权限或磁盘问题执行失败,归档日志不会自动删除,目录涨满后数据库照样会挂起。所以删除操作要配合监控。5.2 用CATALOG与NOCATALOG管理备份小型环境通常用控制文件管理备份(RMAN的NOCATALOG模式),大环境用恢复目录(CATALOG模式)。用控制文件管理时,如果数据文件和控制文件全部损坏,恢复就很麻烦,因为控制文件里没记录备份信息。用CATALOG模式时,RMAN的备份记录存在独立的恢复目录数据库里,即使目标库完全挂了,也能通过恢复目录找到备份文件。条件允许的话,用CATALOG模式最稳。把恢复目录数据库放在另一台机器上,这样目标库和恢复目录不会同时挂。两个模式的操作差别不大,只是多了个连接恢复目录的步骤。6. 高可用环境里的归档日志还原:DG、RAC与恢复的交叉点最后聊一下高可用环境。归档日志还原在高可用环境里,操作逻辑和单机类似,但坑更多。6.1 两套DG库的GAP处理与切换策略热搜词里有两套dg库、主备切换resolvable gap,这些都是实际生产场景。两套DG库意味着一个主库带两个备库(或一套主备加一套级联备库)。处理GAP时,要分别检查每个备库的v$archive_gap,因为不同备库可能缺不同的日志序列号。主备切换的核心是确保备库应用到了主库的所有日志,否则切换后数据会丢失。切换前可以通过select status from v$managed_standby确认备库状态,或者看v$archive_dest_status里的GAP_STATUS字段。如果显示RESOLVABLE GAP,说明丢失的日志还存在于主库归档目录,可以补;如果是UNRESOLVABLE GAP,说明日志已经被清理,切换后数据铁定丢失,这种状态下宁可放弃切换也不能硬切。6.2 RAC环境还原与单实例还原的差异RAC环境还原时,注意所有数据文件都在共享存储上,所以只用在一个节点上执行RMAN操作即可。区别在于,必须先关闭所有实例,再以nomount或mount方式启动一个实例执行还原。另外,spfile必须使用集群文件,不能是本地文件,否则节点起不来。# RAC环境还原注意事项 # 1. 所有节点关闭 srvctl stop database -d orcl # 2. 单节点启动到mount sqlplus / as sysdba SQL startup mount; # 3. 执行还原恢复 RMAN restore database; RMAN recover database; # 4. 完成后用srvctl启动整个集群 srvctl start database -d orclRAC环境恢复的归档日志和应用过程,和单实例没有本质区别。唯一要小心的是,归档日志在不同节点上可能是分散的,恢复前要把所有节点的归档日志集中到一个目录,否则recover database可能会提示找不到某些归档日志。6.3 一个可用性极高的场景思路:备库临时顶替 快速还原原库很多朋友纠结要不要做还原演练,担心影响生产。其实有个非常实用的思路:利用DG备库临时顶替,把原库拿去演练还原。操作流程是:先把备库切换为主库(switchover),让业务在备库上跑,然后原主库降为备库,此时在原主库上做还原演练完全不影响业务。演练完成后,再切换回去。这种做法的前提是业务能接受短时间的切换,以及备库硬件能承受生产负载。但演练带来的价值远超这点风险,至少能保证你在真正出事时,不会在凌晨四点对着RMAN报错发呆。备库顶替、原库还原、再切换回来,这个流程我演练过很多次,熟练之后每次切换控制在10分钟以内,业务几乎无感知。强烈建议有条件的朋友在测试环境先跑一遍完整流程。7. 最后一次提醒:还原结束后必须做的三件事还原结束不代表收工,还有三件事必须立刻做,否则下一次恢复时会吃大亏。第一,做一次完整的数据库备份。无论是resetlogs之后,还是从备份直接还原的库,备份记录已经失效,必须重新全备。否则下次恢复时,你手头的备份文件都是旧的化身,和当前数据库对不上,结果就是无法恢复。第二,确认监听、告警日志、定时任务都正常。数据库能open不代表一切正常,归档目录空间、监听状态、备份任务调度都要重新确认。第三,记录整个恢复过程和耗时。恢复花了多久、用了哪些备份文件、卡在哪个环节,全部记下来。这不仅是复盘依据,也是未来优化恢复策略的第一手数据。归档日志还原这件事,说到底拼的是两样:一是备份策略是否靠谱,二是操作者是否冷静。备份策略有问题,再熟练的操作也救不回来;操作者慌了手脚,再完整的备份也可能被搞砸。保持冷静,按照备份确认、还原、恢复、验证这条链路一步步走,绝大多数故障都能解决。希望这篇实操笔记能让你在真正面对故障时,多一点底气,少一点慌乱。
返回列表