整个过程分为 OS层(操作系统) 和 DB层(数据库) 两个阶段。
第一阶段:OS层排查(发现异常进程)
步骤 1:发现可疑进程
-
目标:查找目标脚本进程。
-
执行命令 :
bashps -ef | grep get_b -
发现 :父进程
102374(/bin/sh)和子进程102376(sqlplus)自Aug31起运行,CPU累计时间为00:00:00。
步骤 2:查看父进程内核态等待状态
-
目标:确认父进程卡在什么内核调用上。
-
执行命令 :
bashps -o pid,stat,wchan=WIDE-WCHAN-COLUMN -p 102374 -
回显 :
S(可中断睡眠) +do_wait(等待子进程退出)。 -
结论 :父进程自身正常,是在等待子进程(
102376)结束。
步骤 3:确认父子进程关系
-
目标:查看父进程下的子进程详情。
-
执行命令 :
bashps -ef | grep 102374 -
发现 :子进程
102376是sqlplus -S(静默SQL*Plus客户端)。
第二阶段:DB层深度排查(推翻OS层"杀进程"的误判)
步骤 4(关键转折):在数据库内查看会话真实状态
-
目标:确认SQL在数据库内部的真实状态(是卡死还是在干活)。
-
执行命令(在SQL*Plus或PL/SQL中以DBA身份执行) :
sqlSELECT s.sid, s.serial#, s.status, s.sql_id, s.event, s.wait_class, s.seconds_in_wait FROM v$session s WHERE s.process = '102376' -- 对应OS进程号 AND s.program LIKE '%sqlplus%'; -
关键发现 :
STATUS=ACTIVE,WAIT_CLASS=User I/O。 -
结论 :数据库正在积极执行I/O操作(读数据/写临时表空间),绝对不能 在OS层执行
kill -9,否则会导致数据库回滚风暴。
步骤 5:查看SQL执行进度(长期操作监控)
-
目标:确认SQL完成百分比。
-
执行命令 :
sqlSELECT opname, target_desc, sofar, totalwork, ROUND(sofar/totalwork*100,2) AS pct_complete, elapsed_seconds, time_remaining FROM v$session_longops WHERE sid = <步骤4查到的SID> AND sofar < totalwork ORDER BY start_time DESC; -
(可能的结果):如果进度不增长,配合步骤6进一步诊断。
步骤 6:确认I/O挂起(最终诊断)
-
目标 :当发现
seconds_in_wait持续增加且不归零时,查看具体等待事件和等待的文件号。 -
执行命令 :
sqlSELECT event, seconds_in_wait, state, p1, p1text, -- p1代表文件号 p2, p2text -- p2代表块号 FROM v$session WHERE sid = <步骤4查到的SID>; -
发现 :
seconds_in_wait持续增长,证明单次I/O请求挂起(存储响应极慢或磁盘故障)。
步骤 7(可选):定位卡住的物理数据文件
-
目标:找出P1文件号对应的具体文件(用于联系存储工程师)。
-
执行命令 (如果P1指向数据文件):
sqlSELECT file_id, file_name, tablespace_name FROM dba_data_files WHERE file_id = <步骤6查到的P1值>; -
(如果指向临时表空间) :
sqlSELECT file_id, file_name, tablespace_name FROM dba_temp_files WHERE file_id = <步骤6查到的P1值>;
步骤 8(可选):抓出正在执行的完整SQL内容
-
目标:保存SQL供后续优化参考。
-
执行命令 :
sqlSELECT sql_fulltext FROM v$sql WHERE sql_id = '<步骤4查到的SQL_ID>';
最终解决方案(执行的动作)
鉴于确诊为 存储I/O挂起 且 sofar 无推进,决定放弃本次执行。禁止使用OS层的 kill -9 102376,改为在数据库端优雅取消SQL。
最终执行命令(在数据库中):
sql
-- 假设步骤4查到的 SID=123, SERIAL#=45678
ALTER SYSTEM CANCEL SQL '123, 45678';
执行效果:
- 数据库立即中止当前I/O调用,会话返回
ORA-01013错误并退出。 - OS层的子进程
102376自动终止,父进程102374收到子进程退出信号,自动结束do_wait并退出。 - 整个进程树彻底清理,且无回滚灾难风险。
附加:事后预防建议(未执行,但建议后续添加)
如果脚本将来需要防止此类长时间挂起,可在OS层脚本中修改,增加 timeout 命令限制SQL*Plus的执行时长:
bash
timeout 3600 sqlplus -S user/pass@db @script.sql
(超过1小时自动强制终止,避免积累僵尸卡住进程。)
总结核心原则 :只要 ps 看到 sqlplus 挂起,第一反应永远不是 kill -9 ,而是进入数据库查 v$session。User I/O 是干活状态,Concurrency 是锁等待,只有当 seconds_in_wait 疯涨且进度为0时才考虑 ALTER SYSTEM CANCEL。