半夜三点,手机震了。我迷迷糊糊摸过去一看,是客户发来的消息,只有四个字:“数据库挂了。”那一刻,我瞬间清醒。做 DBA 这行最怕的就是这种消息,因为 Oracle 数据库一旦宕机,往往意味着业务停摆、数据丢失,甚至可能导致整个公司瘫痪。我赶紧爬起来,连外套都没顾上穿,就开始远程检查。系统日志里满是 ORA-00600 错误,数据文件状态显示 “RECOVER”,控制文件也报了一堆校验不一致。说实话,那时我心里没底,但手不能停,得赶紧拉一份 alert 日志和 trace 文件,看看能不能找出根因。这种时候,时间就是金钱,每一分钟都在烧钱。

先说最常见的故障场景:归档日志丢失。很多企业为了省钱,把归档日志放在便宜的共享存储上,结果磁盘一坏,归档直接没了。这时数据库还能跑,但一旦需要归档恢复,比如做不完全恢复或重做日志切换时,系统就会卡住。有一次,我接手一个客户,他们的归档日志已经连续三天没备份,结果存储挂了,归档目录全空。数据库仍在运行,但 alert 日志里疯狂报 “ORA-00308: cannot access archived log”。客户急得像热锅上的蚂蚁,因为业务系统已经无法进行任何写入操作。常规做法是尝试从备份中恢复归档日志,但客户连备份都没有。我只能用 RMAN 的 “recover database until cancel” 命令,强制数据库跳过丢失的归档,回到一个一致的状态。虽然丢了几小时的数据,但总比整个库报废强。这教训就是:归档日志别省,备份要勤做,不然出事谁都救不了。
再聊数据文件损坏。Oracle 的数据文件是物理存储的核心,一旦某个文件出现坏块,数据库可能直接挂掉。我遇到过最头疼的一次,是客户的一张核心业务表,存储了上百万条交易记录,突然报 “ORA-01578: ORACLE data block corrupted”。查了半天,发现是磁盘有物理坏道,导致某个数据块彻底读不出来。常规恢复手段,比如用 RMAN 的 “blockrecover” 命令,只能修复有备份的块。可这张表太活跃,备份时间点之后写入了大量新数据,坏块正好在新写入的区域。没辙,我只能用 Oracle 的 “dbmsrepair” 包,把坏块标记为逻辑删除,然后通过 “alter table enable row movement” 和 “create table as select” 把好数据捞出来。这招保住了大部分数据,但坏块附近的几行记录永久丢失。事后客户问能否恢复那几行,我只能摇头:物理损坏,神仙也难救。从那以后,我建议所有客户至少每周做一次全库备份,并且启用块跟踪,这样坏块出现时能第一时间定位。
还有一种让人崩溃的情况:控制文件丢失。控制文件相当于 Oracle 数据库的“地图”,没有它,系统连自己有哪些数据文件、日志文件都搞不清。有一次,一个客户的服务器硬盘同时坏了两块,恰好控制文件就放在其中一个盘上。数据库启动时报 “ORA-00205: error in identifying control file”,我登录一看,控制文件目录空空如也。客户说他们做了 RMAN 备份,但只备份了数据文件和归档日志,控制文件没有单独备份。于是出现了麻烦:控制文件丢失后,即使有数据文件备份,也无法告诉 RMAN 数据库的结构。我只能用 “create controlfile” 命令手动重建控制文件,这需要知道所有数据文件和日志文件的路径以及数据库的 DBID。我翻遍了客户的历史日志,找到了 DBID,又逐个查服务器上的文件列表,拼凑出完整的路径。花了将近两个小时才把控制文件建好,数据库重新启动后,又花了一个小时完成恢复。整个过程下来,我后背都湿透了。事后复盘,如果客户启用了控制文件自动备份,或者把控制文件放在不同磁盘上,根本不会出现这种情况。
表空间误删除也是高频事故。很多 DBA 在维护时手滑,一个 “drop tablespace” 下去,几 TB 的数据瞬间蒸发。我见过最离谱的一次,是运维人员在做清理工作时,把业务表空间当成了临时表空间,直接删了。数据库当时还在运行,但所有访问该表空间的会话都报 “ORA-00959: tablespace does not exist”。客户急得打电话时声音颤抖,因为那个表空间里存着当月的财务报表。幸好 Oracle 提供了闪回技术。如果表空间删除时间不长,并且开启了回收站功能,可以用 “flashback tablespace” 命令恢复。但那次客户没开回收站,只能从备份中恢复。我让客户立即停止所有写入操作,然后用 RMAN 把整个数据库恢复到删除前的时间点。数据文件有 2 TB,恢复加应用归档日志花了整整 6 小时。恢复完成后,还得把删除后新产生的数据手工补进去。客户那几天加班到凌晨,我也熬了三个通宵。这事的教训是:任何删除操作前,先确认三遍,而且一定要开启回收站和闪回功能。
重做日志文件损坏同样致命。重做日志记录了所有数据变更,一旦当前正在写入的日志组损坏,数据库会立刻崩溃。我处理过一个案例,客户的日志文件放在一块老旧硬盘上,硬盘有坏道,导致日志文件头损坏。数据库启动时报 “ORA-00354: corrupt redo log block header”,根本起不来。常规做法是尝试用 “alter database clear logfile” 命令清除损坏的日志组。但如果损坏的是当前日志组,清除会导致数据丢失。那次客户运气好,损坏的是非当前日志组,我直接用命令清除了,数据库成功启动。如果是当前日志组,就只能做不完全恢复,数据必然丢失。所以我总跟客户说:重做日志文件别图省事,至少放两组以上,每组分布在不同磁盘上,并且用镜像或 RAID 保护起来。这钱不能省。
还有一种更隐蔽的故障:Undo 表空间损坏。Undo 表空间用于事务回滚和读一致性,一旦它出问题,数据库可能无法正常提交或回滚事务。我遇到过最诡异的一次,数据库运行正常,但执行任何更新操作都报 “ORA-00600 [ktbrmp1]” 错误。查了半天,发现是 Undo 表空间里的一个回滚段头块损坏。这种问题不能直接用 RMAN 恢复,因为 Undo 表空间在数据库打开时无法离线恢复。我只能先创建一个新的 Undo 表空间,修改参数 “undotablespace” 指向新表空间,再删除旧表空间。但旧表空间里还有未完成的事务,删除前必须强制回滚。我用了 “alter system set undoretention=0” 和 “shutdown abort” 强制中断,重启后系统自动回滚了那些事务,整个过程用了 4 小时,业务完全中断。客户事后抱怨,但我也无可奈何:Undo 表空间一旦损坏,只有这条路可走。
说个冷门但棘手的:参数文件丢失。参数文件(spfile 或 pfile)记录数据库的启动配置,比如内存大小、文件路径等。有一次,一个客户误删了 spfile,数据库关机后再也起不来了。启动时报 “ORA-01078: failure in processing system parameters”。我登录一看,$ORACLEHOME/dbs 目录下空空如也。幸好客户有之前的 pfile 备份,我用那个 pfile 启动数据库,再重新生成 spfile。如果没有备份,只能从 alert 日志里拼凑参数,或从同版本的其他数据库复制一个默认 spfile 再手动调整。那次客户运气好,找到了半年前的 pfile 备份,虽然参数有些过时,但核心路径没变,我花了半小时手动调整后,数据库终于启动。这事的教训是:参数文件一定要定期备份,最好放在多个位置,比如 ASM 磁盘组和本地文件系统各存一份。
干这行越久,越觉得数据库故障恢复不是单纯的技术活,而是心理战。每次半夜接到电话,心跳都会加速几分钟。但经验告诉我,冷静下来,按步骤执行,十有八九能救回来。备份是底线,监控是防线,应急方案是一道保险。别等到数据库挂了才想起这些,那时候只能祈祷了。


