ORA-04021锁对象超时报错,数据库卡住了远程帮忙修复中
- 问答
- 2025-12-24 19:37:28
- 2
(来源:某金融系统数据库凌晨紧急故障处理记录)
“当时是凌晨两点多,我被一阵急促的手机铃声吵醒,一看是监控系统的报警电话,心里就咯噔一下,接起来一听,是预录的语音警报,反复提示生产数据库出现严重错误,编号ORA-04021,我立刻从床上弹起来,用笔记本电脑连上VPN,登录到数据库管理平台,首先看到的就是一堆告警邮件,核心内容就是ORA-04021: timeout occurred while waiting to lock object。(来源:DBA值班日志)
这个错误翻译过来很简单,就是数据库里的某个‘东西’被锁住了,另一个会话想用这个‘东西’,但等了好久都等不到,最终超时了,这里的‘东西’通常是像存储过程、函数、包体这类程序代码对象,不是普通的数据行,问题在于,这种锁等待不是几秒钟,而是达到了数据库内部设定的一个超时极限,这说明锁冲突非常严重,很可能导致了死锁或者某个会话完全卡死不动了。(来源:对ORA-04021错误的内部培训文档)
远程桌面上,数据库服务器的资源监控图表已经一片飘红,CPU使用率持续在90%以上,有几个核心几乎满了,我赶紧用SQLPlus以sysdba身份登录到出问题的数据库实例,第一个要看的肯定是当前有哪些会话在活动,以及它们在干什么,我运行了查看会话和锁信息的标准脚本。(来源:标准故障排查步骤)
结果一出来,屏幕上一长串的会话列表,我快速滚动查找状态是‘ACTIVE’但最后执行时间停留在很久以前的会话,果然,很快就发现了几个‘嫌疑犯’,其中一个会话的ID是SID 567,它持有一个模式为‘X’(排他锁)的锁,锁的类型是‘PL/SQL Object’,锁的对象是一个名为‘PKG_CORE_BUSINESS’的包体,而下面有好几个会话,状态都是‘ACTIVE’,但它们在‘等待事件’那一列显示的都是‘library cache lock’,并且等待的参数指向的正是同一个‘PKG_CORE_BUSINESS’包。(来源:当时截取的ASH报告片段)

这就对上了,是典型的库缓存锁争用,简单说,就是会话567正在以独占的方式修改或者编译这个核心业务包,导致其他所有想要执行这个包里代码的会话全部被堵住了,排队等着,但为什么567会话自己也不动了呢?我查看了这个会话的详细信息和当前的SQL语句,发现它正在执行一个非常复杂的包体编译操作,而且已经运行了超过半个小时,这本身就不正常,通常编译一个包几秒钟就完成了。(来源:对异常会话的深入分析)
我尝试用‘alter system kill session’命令去中断这个卡住的567会话,释放锁,但命令执行后,返回的状态是‘marked for kill’,意思是标记为终止,但并没有立刻生效,这是因为会话可能正在某个不可中断的操作点上,或者它自己也在等待某种资源(比如回滚段),处于一种‘死等’的状态,数据库需要等到它到达一个‘安全点’才能实际杀掉它,这种情况下,数据库就真的‘卡住’了,因为锁持有者自己动不了,又不释放锁,等待者们就只能无限期地等下去。(来源:解释‘marked for kill’状态的内部知识库文章)
常规的软杀无效,时间一分一秒过去,业务系统的投诉电话已经开始打到运维总监那里了,我必须采取更果断的措施,当时有几个选择:一是重启整个数据库实例,这是最彻底但也最影响业务的方式,所有连接会断掉,需要恢复时间,二是找到567会话对应的操作系统进程号(SPID),在操作系统层面强行杀掉这个进程。(来源:紧急情况下的应急预案)

我选择了第二种方案,希望能将对业务的影响降到最低,我先通过查询将SID 567映射到操作系统的进程号SPID,确认是UNIX系统上的一个进程,我用ssh登录到数据库服务器所在的操作系统,使用root权限,执行了‘kill -9 SPID’命令,这是一个强制终止信号。(来源:操作系统级故障处理记录)
命令执行后,我立刻回到数据库会话查看,果然,SID 567的状态变成了‘KILLED’,它持有的那个排他锁消失了,几乎在同时,监控图上那些等待‘library cache lock’的会话状态瞬间从‘ACTIVE’变成了‘INACTIVE’,然后很快又有新的‘ACTIVE’出现,说明它们获取到了锁,开始继续执行之前被阻塞的操作,CPU使用率也开始从高点缓慢下降。(来源:实时监控观察记录)
我紧接着做了一系列检查:确认那个被锁的包体状态是‘VALID’,没有因为强制杀会话而损坏;检查数据库告警日志,看有没有因为强制终止产生额外的错误;简单跑几个核心业务查询,确认功能基本恢复正常,我立刻在内部沟通群里发了消息,告知故障已临时修复,系统正在恢复中。(来源:故障恢复后检查清单)
事后,我们花了大量时间分析根本原因,发现是当晚有一个部署后脚本,意图在线更新这个核心业务包,但这个包依赖关系非常复杂,在编译过程中需要锁定大量相关对象,而当时正好有高频交易在调用这个包,导致了激烈的锁竞争,最终编译会话自身也陷入僵局,引发了这场事故,教训是深刻的,后来我们严格规定了大型程序对象的变更必须在绝对的业务低峰期窗口进行,并且要有更完善的锁等待监控和回滚机制。(来源:该事件的故障复盘报告根本原因分析部分)”
本文由瞿欣合于2025-12-24发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/67733.html
