标签: Oracle, 数据库, 生产事故, DBA, 故障排查, 性能优化, 运维
前言
每个DBA的职业生涯中,总有那么几个不眠之夜,让你心跳加速,让你在海量日志和监控数据中奋力寻找那一线生机。今天,我想分享一次我亲身经历的、堪称教科书级别的Oracle生产事故。它从一个最常见的“CPU使用率100%”告警开始,最终却揭示了一个隐藏在系统深处的“定时炸弹”。我希望通过这次复盘,能给所有奋战在一线的技术同学带来一些启发和警示。
一、事故背景:风平浪浪静的周五下午
-
时间: 一个周五的下午16:30
-
系统: 公司核心CRM(客户关系管理)系统
-
数据库: Oracle 19c RAC (双节点),部署在Linux服务器上
-
业务特征: 7x24小时运行,读写并发高,是公司的生命线系统之一。
那天下午,一切如常。我正在整理下周的数据库巡检报告,心里盘算着一个轻松的周末。然而,运维的宿命就是,你越是计划周末,麻烦就越是会找上门。
二、告警响起
16:45,监控系统Zabbix突然爆出一连串刺耳的告警:
[High] Oracle Node 1: CPU Utilization > 95% for 5 mins
[High] Oracle Node 2: CPU Utilization > 95% for 5 mins
紧接着,应用团队的电话就打了进来:“我们的CRM系统响应非常慢,很多用户反馈页面卡顿,甚至无法登陆!”
我的心一沉,知道这绝对不是小问题。双节点CPU同时飙高,意味着整个数据库集群都处于高压状态。
三、第一轮排查:CPU、锁与慢SQL
这是DBA处理性能问题的标准流程,但这次,它却把我带入了一个“兔子洞”。
Step 1:确认服务器状态
我立即SSH登录到两台数据库服务器,执行 top 命令。果然,oracle 进程几乎吃满了所有CPU核心,us(用户空间CPU)和 sy(内核空间CPU)都异常高。
# top 命令输出摘要
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12345 oracle 20 0 30.5g 25.1g 24.9g R 98.7 78.5 123:45.67 oracle_p001_...
12346 oracle 20 0 30.5g 25.1g 24.9g R 95.4 78.5 122:33.11 oracle_p002_...
... (大量Oracle进程CPU占用率极高)
Step 2:进入数据库,寻找元凶
我以 sysdba 身份登录数据库,开始排查会话和等待事件。
-- 查询当前活跃会话及其等待事件
SELECT sid, serial#, username, status, event, sql_id
FROM v$session
WHERE status = 'ACTIVE' AND type != 'BACKGROUND';
查询结果让我眉头紧锁。大量的应用会话处于 ACTIVE 状态,但等待事件却五花八门,最突出的是 latch free 和 log file sync。
-
latch free:通常意味着内部锁(latch)的严重争用,CPU飙高时很常见。
-
log file sync:表示用户session在等待LGWR(日志写入进程)将redo log buffer中的数据写入在线重做日志文件。这个等待事件的普遍出现,是一个非常危险的信号。
Step 3:AWR报告分析
我手动生成了一份过去15分钟的AWR(自动工作负载信息库)报告。报告结果如下:
-
Top Timed Events:DB CPU 排名第一,log file sync 和 latch: cache buffers chains 紧随其后。
-
Top SQL by CPU Time:排名第一的是一条非常简单的 UPDATE 语句,来自CRM的核心模块。这条SQL本身逻辑简单,执行计划也走了索引,平时执行效率极高。
这条 UPDATE 语句本身没有问题,但由于它被海量并发调用,在数据库整体性能恶化的情况下,它成了CPU消耗的“背锅侠”。
此时,应用团队、领导的电话和IM消息不断涌来,压力巨大。我尝试杀掉几个持有锁时间最长的会话,但无济于事,新的会话马上又会遇到同样的问题。
排查陷入了僵局。CPU高、latch争用、log file sync等待,这些都是症状,但真正的病根是什么?
四、峰回路转:从日志切换到归档空间
在巨大的压力下,我强迫自己冷静下来,重新审视 log file sync 这个等待事件。它意味着LGWR写入日志变慢或受阻。为什么会受阻?
我立刻去查看数据库的 alert.log(告警日志),这是Oracle DBA的“黑匣子”,记录了数据库运行的所有关键事件和错误。我从后往前翻,很快,一条关键的错误信息映入眼帘:
Fri Oct 27 18:35:10 2023
ARC3: Archiving not possible: No primary destinations available
...
Fri Oct 27 18:35:15 2023
Errors in file /u01/app/oracle/diag/rdbms/orcl/ORCL1/trace/ORCL1_arc2_34567.trc:
ORA-19815: WARNING: db_recovery_file_dest_size of 536870912000 bytes is 100.00% used, and has 0 remaining bytes available.
真相大白了!
问题的原因是:数据库的快速恢复区(Fast Recovery Area, FRA)空间被100%占满了!
事故的逻辑链条是这样的:
-
某个操作产生了海量的 redo 日志。
-
在线重做日志(Online Redo Log)被迅速写满,需要频繁切换并进行归档。
-
归档进程(ARCH)尝试将写满的在线日志归档到FRA。
-
由于FRA空间已满(ORA-19815),归档进程失败并挂起 (Archiving not possible)。
-
由于归档进程无法归档,写满的在线日志文件不能被覆盖和重用。
-
LGWR进程在等待可用的在线日志文件,无法继续写入,导致所有提交(COMMIT)操作都在等待 log file sync。
-
大量的用户会话因为无法完成提交而挂起,持有各种资源(如latch),导致了严重的latch争用。
-
新来的会话请求资源时,因争用而空耗CPU,最终导致整个数据库集群CPU 100%,系统濒临崩溃。
这个原因解释了所有症状!CPU高是结果,log file sync 是中间环节,而 FRA空间耗尽 才是真正的罪魁祸首!
五、深挖根因:失控的“数据清理”定时任务
为什么FRA会突然被撑爆?我立刻检查归档日志的生成速度。
-- 查看每小时归档日志生成量
SELECT TO_CHAR(completion_time, 'YYYY-MM-DD HH24') AS hour,
ROUND(SUM(blocks * block_size) / 1024 / 1024 / 1024, 2) AS gb
FROM v$archived_log
WHERE completion_time > SYSDATE - 1
GROUP BY TO_CHAR(completion_time, 'YYYY-MM-DD HH24')
ORDER BY hour;
结果令人震惊。从下午16:00开始,归档日志的生成量从平时的每小时10GB左右,飙升到了 150GB/小时!
我立刻联系了应用开发负责人,询问今天下午是否有任何上线或变更。一开始他们也一头雾水,但在我的追问下,一位年轻的开发工程师想了起来:
“哦,对了,我们今天下午上线了一个功能优化。为了解决CRM历史数据过多的问题,我们写了一个数据清理的存储过程,并通过DBMS_JOB设置了每小时执行一次,第一次执行时间就是下午16:00。”
我让他把代码发我。一看代码,我差点晕过去。这个清理任务的逻辑是:
这是一个一次性删除海量数据的大事务!HUGE_TRANSACTION_TABLE 是一个有数亿行记录的流水表。这个 DELETE 操作没有分批,没有加 NOLOGGING 选项(虽然对DELETE无效,但体现了开发者的意识),一次操作删除了上千万行数据,产生了天文数字般的 redo 和 undo,瞬间就把FRA给撑爆了。
六、紧急处理与业务恢复
原因找到了,接下来就是与时间赛跑。
-
紧急联系应用团队: 立刻停止那个该死的 DBMS_JOB!EXEC DBMS_JOB.BROKEN(job_id, TRUE);
-
释放FRA空间:
-
登录RMAN,执行 CROSSCHECK ARCHIVELOG ALL; 确认物理文件与目录信息一致。
-
执行 DELETE NOPROMPT EXPIRED ARCHIVELOG ALL; 删除过期的归档日志。
-
最关键的一步:在确保备份有效的前提下,手动删除一部分较早的归档日志来快速腾出空间。DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-2'; (删除2天前的归档)。
-
-
监控恢复: FRA空间被释放后,挂起的ARCH进程立刻恢复工作,开始疯狂归档。
-
alert.log 中不再出现 Archiving not possible 的错误。
-
v$session 中的 log file sync 等待事件迅速减少。
-
服务器的CPU使用率在10分钟内从100%降至了30%以下的正常水平。
-
-
业务验证: 应用团队确认,CRM系统恢复正常,用户可以正常登录和操作。
20:30,在经历了4个小时的紧张抢修后,系统终于恢复了平稳。办公室里响起了疲惫而又如释重负的掌声。
七、复盘总结与改进措施
这次事故虽然最终得以解决,但暴露出的问题是深刻的。我们在事后的复盘会上,制定了以下改进措施:
-
技术层面:
-
DBA: 将FRA使用率、归档日志生成速率加入核心监控项,设置更灵敏的告警阈值(例如,使用率超过80%就告警)。
-
开发:
-
严禁在生产环境中使用未经充分测试的大事务SQL。
-
所有涉及大量数据操作(DELETE, UPDATE, INSERT)的代码,必须采用分批、小事务的方式进行,并加入适当的等待间隔,避免对数据库造成冲击。例如,使用 LOOP 循环,每次处理1000条记录后 COMMIT。
-
加强对开发人员的数据库基础知识培训,让他们理解redo, undo的机制和影响。
-
-
-
流程层面:
-
变更管理: 任何涉及数据库的变更,无论大小,都必须经过DBA的评审(Code Review)。特别是定时任务和数据处理脚本。
-
测试流程: 类似的数据清理任务,必须在与生产环境数据量级相当的预发环境中进行充分测试,评估其对数据库性能(IO, CPU, Redo)的影响。
-
应急预案: 完善此类故障的应急预案(SOP),当log file sync或FRA告警出现时,能有更快的反应路径。
-
结语
这次事故像一面镜子,照出了我们在技术、流程和沟通上的盲点。它提醒我们,在复杂的系统中,任何一个微小的、看似无害的改动,都可能通过一系列链式反应,最终演变成一场灾难。
排查问题时不能只看表面现象(如CPU高),而要深入挖掘等待事件背后的逻辑,alert.log 永远是你的第一手信息来源。对于开发同学,要敬畏生产环境,你写的每一行代码,都在“看不见”的底层消耗着实实在在的资源。
841

被折叠的 条评论
为什么被折叠?



