怎么一步步把Oracle里卡住的死锁进程给关了,操作细节讲清楚点
- 问答
- 2025-12-27 00:25:47
- 2
你得明白死锁是啥,简单说,就是两个或者更多的事务,你等我,我等你,互相掐着对方需要的资源,谁也不肯先松手,结果大家都卡在那里动不了,Oracle数据库自己有个后台侦探,叫“死锁检测器”,它每隔几秒钟就会巡查一次,一旦发现这种互相掐架的情况,它会主动跳出来当裁判,选择其中一个事务作为“牺牲品”,把它回滚掉,从而让其他事务能继续运行,并且会在数据库的日志文件里记下一笔,说“某年某月某日,我处理了一个死锁”。
有时候这个自动处理可能不够及时,或者你想手动干预,快速解决问题,这时候就需要人工介入了,整个过程可以分成三步:第一步,找到谁是“罪魁祸首”;第二步,确认细节,避免杀错人;第三步,执行关闭操作。
第一步:找到锁的源头和死锁的会话
你需要以有管理权限的用户登录到数据库,比如SYSTEM用户或者更好的具有DBA权限的用户,打开SQL*Plus或者你习惯用的SQL开发工具。
你要查看当前数据库里有哪些锁是阻塞别人(被别的会话等待)的,可以执行下面这个查询语句(来源:Oracle官方文档中关于动态性能视图V$LOCK和V$SESSION的常用查询):
SELECT
l1.sid AS blocking_sid,
s1.username AS blocking_user,
s1.machine AS blocking_machine,
s1.program AS blocking_program,
l2.sid AS waiting_sid,
s2.username AS waiting_user,
s2.machine AS waiting_machine,
s2.program AS waiting_program
FROM
v$lock l1,
v$lock l2,
v$session s1,
v$session s2
WHERE
l1.block = 1
AND l2.request > 0
AND l1.id1 = l2.id1
AND l1.id2 = l2.id2
AND l1.sid = s1.sid
AND l2.sid = s2.sid;
这个查询结果会清晰地显示两列关键信息:一列是“blocking_sid”(正在阻塞别人的会话ID),另一列是“waiting_sid”(正在等待的会话ID),死锁通常涉及至少两个这样的会话互相阻塞,你需要重点关注那些“blocking_sid”,它们就是导致问题的一部分,你需要找出那个最根源的阻塞者,或者直接处理参与死锁的任意一个会话来打破僵局。
第二步:确认会话的详细信息

从上一步你拿到了一个或多个可疑的会话ID(SID),你需要进一步确认这个会话具体在干什么,是谁发起的,来自哪台机器,运行的是什么程序,这能帮你最后确认一下,关掉它是不是安全的,会不会影响重要的业务,用下面这个查询,把刚才找到的SID替换进去(比如这里用123做例子)(来源:基于V$SESSION视图的常用信息查询):
SELECT
sid,
serial#,
username,
machine,
program,
module,
action,
logon_time,
status,
sql_id,
prev_sql_id
FROM
v$session
WHERE
sid = 123;
这里有个极其重要的信息叫“SERIAL#”(序列号),你光有SID是不够的,因为Oracle可能会重用SID,SERIAL#和SID一起,才能唯一确定一个会话在某个时间点的状态,所以你待会儿要关闭会话时,必须同时提供SID和SERIAL#。
第三步:关闭死锁的会话
确认无误后,就可以动手关闭会话了,关闭会话有两种方式:一种是温和的,一种是强硬的。
温和的方式:使用ALTER SYSTEM KILL SESSION命令。

这个命令是首选,它的作用是告诉数据库,“请优雅地终止这个会话”,数据库会通知那个会话,让它自己主动回滚正在进行的事务,然后退出,这能保证数据的一致性,命令的格式如下(来源:Oracle SQL语言参考中ALTER SYSTEM语句):
ALTER SYSTEM KILL SESSION 'sid,serial#';
把第二步查到的SID和SERIAL#填进去,比如SID是123,SERIAL#是45678,那么命令就是:
ALTER SYSTEM KILL SESSION '123,45678';
执行这个命令后,你可能会发现那个会话的状态在V$SESSION视图里变成了“KILLED”,如果那个会话正在进行一个非常大的事务(比如更新上百万条数据),回滚可能需要很长时间,你会看到它处于“标记为终止/回滚”的状态,这时候你需要耐心等待它自己完成回滚,你可以通过查询V$SESSION视图的STATUS列和LAST_CALL_ET列(表示最后一次调用经历了多少秒)来观察进度。
强硬的方式:在操作系统层面“杀进程”。
如果温和的方式失效了(比如会话卡住,就是不回滚),或者情况非常紧急,等不及回滚,你就需要更狠的手段,这需要你登录到数据库服务器所在的操作系统上(通常是Linux/Unix)。

你需要在数据库里找到这个会话对应的操作系统进程ID(PID),再用一次V$SESSION视图:
SELECT
sid,
serial#,
process AS oracle_process_id
FROM
v$session
WHERE
sid = 123;
这里查到的oracle_process_id就是数据库后台进程的标识,在Linux/Unix系统上,你还需要找到它对应的操作系统进程PID,通常可以通过查询V$PROCESS视图关联得到,但更直接的方法是,在操作系统下使用ps命令结合grep来查找。
ps -ef | grep ora_ | grep -i <刚才查到的oracle_process_id>
找到对应的操作系统进程PID(比如是98765)后,使用kill命令,先尝试温和的kill(发送TERM信号):
kill 98765
如果不行,再使用强制的kill(发送KILL信号,参数-9):
kill -9 98765
!重要警告! 使用kill -9是最后的选择,非常暴力,它相当于直接拔电源,可能会导致数据库需要一段时间进行崩溃恢复,极端情况下甚至有极小概率造成数据损坏,除非万不得已,不要轻易使用kill -9。
务必验证一下。
执行完关闭操作后,最好再跑一遍第一步的查询,看看那个阻塞链是否已经消失,同时检查一下相关的应用程序是否已经恢复正常。
核心步骤就是:登录数据库 -> 查锁找SID -> 查会话详情拿SERIAL# -> 先用ALTER SYSTEM KILL SESSION命令 -> 如果不行,再考虑操作系统级kill,整个过程要小心谨慎,确认无误后再操作,尤其是生产环境。
本文由瞿欣合于2025-12-27发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/69095.html
