凌晨两点,电话铃声比任何闹钟都刺耳。“asmcmd卡死了,巡检脚本全卡住,手动敲lsdg也没反应。”电话那头的声音明显已经发慌了。我揉着眼睛坐起来,脑子里瞬间闪过一丝熟悉的不安——凡是asmcmd这个入口出了问题,后面所有操作都会像多米诺骨牌一样倒下。磁盘组状态怎么看,rebalance进度怎么查,恢复演练怎么做,全都依赖这一个小小的命令行工具。
这篇文章不打算讲asmcmd的常规用法,那本官方文档已经写得很清楚了。我想完整复盘一次“asmcmd彻底卡死”的定位过程:从现象识别、边界判断、strace追踪、ASM实例内部诊断,到最终根因确认和应急恢复,把每一步的思考逻辑和可用命令都摊开来讲。适合每天跟Oracle和ASM打交道的DBA、运维工程师,也适合对ASM底层工作方式感兴趣的人。对新手来说,这套排查思路本身就是一套可以反复复用的方法论。
我尽量把判断依据写细一点,包括很多官方文档里不会写的“体感经验”,比如什么情况下不要急着kill,什么情况下必须果断重启。遇到这种问题最怕的不是找不到原因,而是在慌乱中做出了错误的操作,把小问题放大成事故。
1. 卡死现场:asmcmd表现出的几种“假死”与“真死”
1.1 我遇到的现场还原
先说我自己遇到的一次场景。当时是在做存储扩容的收尾检查,准备用asmcmd确认新LUN设备是否已经成功加入磁盘组。我输入了最常用的命令:
bash复制asmcmd lsdg
回车之后,终端没有任何输出,光标一直闪烁。我等了一会儿,依然没有反应。第一反应是终端卡了,又开了一个新的SSH会话试同样的命令,结果依旧卡住;再试asmcmd -p进入交互模式,同样停在启动阶段。这时候基本可以确定:问题不在终端、不在本地网络,而是asmcmd在连接或者初始化ASM环境的过程中,被什么东西卡住了。
这里有一个非常关键的判断点:asmcmd卡住不等于ASM实例不可用。需要先确认是只有asmcmd这一个入口受影响,还是ASM实例整体已经无响应。这个边界判断决定了后续是走“轻量级排查”还是“紧急抢救”的路径。我在现场的做法是先看进程状态、再用sqlplus直连ASM、最后才动集群相关的东西。
另一个值得关注的细节是:asmcmd是启动阶段就卡住,还是执行特定命令时才卡住。如果连help都出不来了,说明客户端初始化环节出问题;如果其他命令正常但ls -l卡住,那就多半和磁盘元数据读取有关。这个差异直接决定了排查方向。
1.2 假死与真死的区分方法
我在现场第一时间做了三件事:看asmcmd进程的状态、用sqlplus连ASM实例、查看系统负载。这三个结果组合起来,基本能把问题框定在一个小范围内。
bash复制# 查看asmcmd相关进程状态
ps -ef | grep asmcmd | grep -v grep
# 查看进程是否为D状态,D = 不可中断睡眠,通常意味着等待IO
ps -o pid,stat,wchan:30,cmd -p <asmcmd_pid>
这里的STAT列是关键:如果进程处于R或S状态,说明它在等待CPU或者等待某个事件,属于“逻辑上卡住”;如果处于D状态,那就是在等待IO完成,这种状态下kill -9都杀不掉,只能等底层IO恢复或者超时。D状态进程的wchan列通常会显示等待的内核函数名,比如wait_on_page_bit、submit_bio、scsi_execute之类,一看就知道是IO层面的问题。
第二条是用sqlplus直接连ASM实例:
bash复制sqlplus / as sysasm
如果这条命令也卡住,说明ASM实例内部的资源已经出了大问题,问题范围比单纯asmcmd卡住要大得多;如果能秒进,说明ASM实例本身是健康的,卡住的原因多半出在asmcmd这个客户端程序的初始化或者连接过程。把这两种情况分开,是避免“乱杀进程”的第一步。
第三条是看系统负载:
bash复制top -c
这一步能帮你判断是CPU超载引起的饥饿,还是IO等待引起的全面卡顿。如果CPU占用率不高,load average却很高,D状态进程数很多,那几乎可以确定是IO层面的故障;如果CPU跑满而asmcmd只是排在队列里,那问题方向又不一样了。
1.3 为什么asmcmd卡死会让人格外紧张
说实话,asmcmd卡死和普通的SQL语句卡死,给人带来的压迫感完全不是一个量级。普通的SQL卡死,你至少还有ALTER SYSTEM KILL SESSION这个手段可以试,还有其他会话可以继续工作。但asmcmd是DBA在操作系统层面操作ASM实例的主要入口,它一旦卡死,你连“看磁盘组里有什么文件”这种最简单的操作都做不了。
更麻烦的是,asmcmd卡死经常伴随着连锁反应。比如巡检脚本挂起,后续的任务全部排队;比如恢复演练被打断,时间窗口白白浪费;再比如你在asmcmd里发起的一个操作其实已经提交到ASM实例了,但客户端没有返回结果,你不知道到底执行到哪一步,只能等。这种不确定性才是最折磨人的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从命令行到实例内部:一条完整的排查链路
2.1 第一层:asmcmd进程到底在等什么
既然怀疑是asmcmd自身卡住,抓一下它的系统调用和调用栈是最直观的做法。strace能告诉我这个进程当前的系统调用卡在哪里,是read还是connect,还是ioctl。
bash复制# 用strace跟踪asmcmd进程,观察卡在哪个系统调用
strace -p <asmcmd_pid> -tt -T -f -o /tmp/asmcmd_strace.log
我跟踪了一段时间后发现,asmcmd卡在了一个文件IO相关的系统调用上,反复抓了几次pstack,看到的调用栈也指向了IO等待。这个现象很重要:说明asmcmd并不是卡在启动阶段的网络解析,而是卡在了真正读取数据的过程中。再结合前面看到的进程D状态,基本可以锁定是存储IO这条链路出了问题。
strace的输出文件会增长得很快,所以跟踪时间不要太长,抓到关键线索就停掉。如果系统上有lsof,也可以顺手看一眼asmcmd进程打开了哪些文件:
bash复制lsof -p <asmcmd_pid> | head -30
看看它是不是正在读某个配置文件、某个挂载点,或者某个日志文件。有时候进程会一直打开某个文件句柄不放,这通常意味着它在等待这个文件对应的底层设备完成IO操作。
2.2 第二层:ASM实例内部是否有会话卡住
asmcmd本质上是通过SQL连接ASM实例的客户端,所以下一步直接进ASM实例里面查session和等待事件。
sql复制-- 查看当前所有非空闲等待的会话
SELECT inst_id, sid, serial#, username, program, event, wait_class, state
FROM gv$session
WHERE state = 'WAITING'
AND wait_class <> 'Idle'
ORDER BY inst_id, sid;
从输出里,我看到asmcmd对应的会话正卡在一个与ASM后台进程密切相关的等待事件上。顺便查一下磁盘组的整体状态,以及有没有长时间运行的ASM操作:
sql复制-- 查看磁盘组状态
SELECT group_number, name, state, type, total_mb, free_mb
FROM v$asm_diskgroup;
-- 查看是否有长时间运行的ASM操作(rebalance / resync / 检查)
SELECT group_number, operation, state, power, actual_power, est_minutes
FROM gv$asm_operation;
如果v$asm_operation里有一条长时间未完成的rebalance记录,那几乎可以怀疑是rebalance操作和asmcmd的元数据操作之间产生了锁竞争。ASM的rebalance是一个后台行动,但它在移动数据的同时也会频繁访问磁盘目录、文件目录等元数据,如果rebalance还带着高power运行,对前台命令的影响会非常明显。
我还习惯查一下v$asm_client,看看当前有哪些客户端连接着ASM实例:
sql复制SELECT group_number, instance_name, db_name, status
FROM v$asm_client;
很多DBA会忽略这个视图,但它有时候能提供重要线索:如果某个数据库实例对ASM的连接状态不正常,也可能导致ASM实例里出现各种奇怪的等待。
2.3 第三层:操作系统层面的排查重点
在ASM实例信息之外,操作系统层面的CPU、IO和网络状态也不能忽略。我当时执行了以下命令:
bash复制# 查看CPU和整体负载
top -c
# 查看IO状况,重点关注await和svctm
iostat -x 1 5
# 查看内存与swap情况
vmstat 1 5
这些输出能告诉我:系统的CPU是否被打满、磁盘IO是否有大量等待、内存是否不足。如果iostat显示某个设备的avgqu-sz(平均请求队列长度)特别大、await(平均IO响应时间)异常高,而其他设备正常,那问题大概率出在那个存储设备上。
多路径状态也要确认一下:
bash复制multipath -ll
多路径配置出问题的时候,比如路径失效但没有从路径组中剔除,IO会在失效链路上反复超时重试,表现出来就是进程陷入D状态,怎么都杀不掉。我在实际工作中遇到过好几次“存储侧一条链路光纤松动”导致的诡异卡顿,系统日志里全是链路错误,但应用侧只表现为进程无响应。
2.4 第四层:集群层面的交叉验证
在RAC环境下,ASM实例和集群资源(CRS、CSS、EVMD)耦合得很深。asmcmd启动时可能需要访问OCR或者OLR文件,如果集群资源状态异常,也会导致asmcmd在初始化阶段hang住。所以我查看了集群资源状态和关键日志:
bash复制crsctl stat res -t
然后再检查CRS、CSS的日志。CSS日志路径一般在$GRID_HOME/log/<hostname>/cssd/下,重点关注ocssd.log和alert相关文件,看有没有节点驱逐、心跳超时之类的信息。
日志里如果有大量关于DNS解析失败的记录,那就很说明问题了。虽然我们很少把“主机名解析”和“asmcmd卡死”联系起来,但在一些特定版本里,ASM实例和CSS通信时会尝试对主机名做反解,如果/etc/hosts没有配好而DNS服务器又不可达,整个过程会一直等到超时,表现就和asmcmd卡死一模一样。
3. 逼死asmcmd的四种典型机制与各自特征
3.1 机制一:磁盘IO挂起,进程跌入D状态
这是最“硬”的一种卡死方式。ASM在后台有多个关键进程:RBAL负责rebalance协调,GMON负责磁盘组监控,ARBx执行实际的平衡操作。当asmcmd执行lsdg或者ls -l时,ASM实例需要读取磁盘组的元数据,比如磁盘目录、Alias目录信息。如果底层存储链路出了问题,比如光纤断了但多路径软件没有及时切换,或者存储控制器卡死,这些读请求就会一直挂着不下来。
进程表现就是D状态,后面堆积一堆IO等待。此时杀进程没有任何意义,先恢复存储链路才是正路。等存储恢复后,D状态进程一般会自动结束或报错退出;如果还不退,只能考虑重启节点或实例。
这里有个实用经验:看v$asm_disk里每块磁盘的累积IO时间,可以辅助判断到底哪块盘出了问题:
sql复制SELECT group_number, disk_number, name, path,
reads, writes, read_time, write_time
FROM v$asm_disk
ORDER BY group_number, disk_number;
如果某块磁盘的read_time/reads比值异常高,说明这块盘的IO响应速度极慢,卡顿的根源很可能就是它。
3.2 机制二:rebalance/resync操作的锁竞争
ASM实例里有很多锁,针对磁盘组、针对文件目录、针对Alibaba目录。rebalance操作可能会持有某个磁盘组的排他锁,而asmcmd的ls -l命令需要获取同组磁盘的共享锁,两者发生冲突,后者只能进入等待状态。
等待事件上往往能看到和enq: ASM相关的信息。如果v$asm_operation里rebalance的est_minutes特别大,说明rebalance可能要跑好几个小时,期间一些asmcmd的读写操作都可能卡顿甚至卡死。
这种机制的典型特征是:进程不是D状态,而是S状态;在strace里可能看到它一直做futex等待或者sched_yield;ASM实例的会话里能看到enq队列等待。这种状况的解决办法是降低rebalance的power,给前台操作让路:
sql复制ALTER DISKGROUP DATA REBALANCE POWER 1;
前提是磁盘组当前处于allow rebalance的状态,并且你能接受rebalance变慢。在业务高峰遇到前台命令卡住时,可以临时把power调低,等操作完成再调回去。
3.3 机制三:ASM实例内部的异常循环
ASM实例内部如果有某个后台进程异常,比如某条SQL导致ASM实例的library cache频繁失效、某个进程死循环占满CPU,也会让asmcmd的请求无法被及时响应。这种情况下,v$session里可能看不到明显的等待事件,但ASM实例的CPU使用率会异常高。
判断方法是看ASM实例的alert日志:
bash复制tail -200 $GRID_HOME/log/<hostname>/alert_+ASM1.log
如果在卡死时间段前后有ORA-00600、ORA-07445之类的内部错误记录,那几乎可以确定是ASM实例内部发生了异常。这种问题处理起来比较麻烦,一般需要先杀掉异常会话,严重时只能重启ASM实例。
还有一种比较特殊的情况:ASM实例的SGA过小,导致共享池和buffer cache频繁挤兑。ASM实例虽然轻量,但在大磁盘组、文件数量庞大的场景下,元数据cache的压力也不小。如果ASM实例频繁出现等待buffer busy waits或library cache lock,也要考虑是不是需要调大SGA。
3.4 机制四:客户端生命周期里的DNS、OCR与目录权限隐患
asmcmd启动时并不是直接就能连上ASM实例,它需要完成一系列初始化:读取本地配置、确定cluster配置、访问OCR/OLR、解析主机名、登录ASM实例。这些环节中任何一个比较慢或者卡住,asmcmd都会表现为“光启动不干活”。
比如/etc/hosts里没有配置主机名的反解条目,而DNS服务器又恰好不可达,某些检查环节就会一直等到超时。还有过这样的案例:$GRID_HOME目录所在文件系统空间满了,asmcmd启动时写不了调试日志,也会卡死。所以遇到asmcmd卡死,记得顺手做这几个检查:
bash复制df -h
echo $ORACLE_HOME
echo $ORACLE_SID
cat /etc/hosts | grep $(hostname)
ls -l $GRID_HOME/cdata/
OCR/OLR文件如果被锁或者损坏,asmcmd初始化阶段就会hang住。有一次我在排查某个节点asmcmd卡死问题时,发现crsctl stat res -t本身也卡住了,最后查出OLR文件权限不对,导致grid用户无法读取。那一次的经验教训是:先确认crsctl能用,再谈检查asmcmd,因为asmcmd的部分初始化逻辑是依赖集群层的。
4. 应急处置:从“保住操作通道”到“恢复数据访问”
4.1 用SQL保住操作通道:asmcmd的替代方案
如果确认ASM实例本身还能响应sqlplus,最直接的恢复操作能力的方法就是用SQL做替代。很多asmcmd能做的事,其实在SQL里都有对应的视图和命令。
sql复制-- 替代 asmcmd lsdg
SELECT name, state, type, total_mb, free_mb
FROM v$asm_diskgroup;
-- 替代 asmcmd ls -l 查看磁盘组下文件
SELECT file_number, type, blocks, bytes, space, name
FROM v$asm_alias
WHERE group_number = 1;
-- 查看ASM磁盘信息
SELECT group_number, disk_number, name, path, mode_status, state, total_mb, free_mb
FROM v$asm_disk;
-- 查看当前挂载的客户端
SELECT group_number, instance_name, db_name, status
FROM v$asm_client;
-- 查看磁盘组成员与路径
SELECT group_number, disk_number, path, header_status
FROM v$asm_disk;
这套SQL替代方案在asmcmd异常时几乎是救命稻草。尤其在自动化脚本里,我后来一直建议团队把asmcmd相关的巡检逻辑改成SQL实现,少一个二进制客户端依赖,脚本就多一分稳定性。
4.2 D状态进程的处理原则
如果确认是D状态进程堆积、底层IO挂起,处理方法是先找存储故障点并恢复,然后观察进程是否自动消失。不要对D状态进程执行kill -9,因为内核根本不会响应,进程会一直停留在数据库的io_cancel状态;更危险的是,如果这个进程持有一份关键的锁或者信号量,在它没被回收之前,后续新起的进程也可能被阻塞。
多路径状态检查、光纤链路检查、存储控制器状态检查,这三样是处理D状态问题时的“三板斧”。在存储故障恢复之前,你做的任何进程操作都是无效功。故障恢复后,D状态进程一般会在几十秒内自行消失,随后asmcmd会报IO错误退出,重新执行就好。
4.3 何时才能动ASM实例重启这个大杀器
如果ASM实例本身卡死,而且已经影响到数据库实例的正常工作,那就要考虑重启ASM实例了。但重启ASM实例前必须确认影响范围:所有依赖该ASM实例的数据库实例都会受影响。这不是可以随便执行的操作,必须和业务方确认维护窗口。
在维护窗口内,可以这样优雅地操作:
bash复制# 停掉该节点上的ASM实例
srvctl stop asm -n <hostname>
或
bash复制crsctl stop res ora.asm -n <hostname>
然后启动:
bash复制srvctl start asm -n <hostname>
如果整个集群已经处于不健康状态,可能还需要通过crsctl stop crs和crsctl start crs来重启整个集群层。但这是最后一个手段,影响面更大,一定要在充分确认之后再做。
还有一个建议:如果ASM实例卡死到连srvctl都执行不了,可以直接尝试:
bash复制sqlplus / as sysasm
shutdown abort;
startup;
shutdown abort对ASM实例来说,是一种能接受的手段,因为ASM实例本质上是一个轻量级的实例,恢复速度通常比数据库实例快得多。但要注意,必须在维护窗口或业务低峰期做。
5. 根治与预防:让asmcmd不再变成定时炸弹
5.1 监控加固:超时、告警、日志三件套
第一道防线是给所有asmcmd命令加超时。不要裸奔执行asmcmd,套一层timeout:
bash复制# 10秒没返回就报警
timeout 10 asmcmd lsdg || echo "asmcmd lsdg timeout"
超时后报警、留日志,至少不会让巡检脚本永远挂在那里。第二道防线是盯住ASM实例的关键等待事件和v$asm_operation,一旦rebalance运行超过预期时间或者出现异常等待,就触发告警。第三道防线是一定要把ASM的alert日志、CSS日志、系统日志统一收集起来,配合监控平台做关键字告警,比如ORA-00600、ORA-00474、node eviction之类。
5.2 环境加固:hosts、空间、环境变量检查清单
很多asmcmd卡死其实是可以提前避免的。我整理了一份简单的环境检查清单,建议每季度执行一次:
bash复制# 1. hosts文件包含所有节点主机名
cat /etc/hosts
# 2. grid用户环境变量正确,ORACLE_SID指向+ASM
echo $ORACLE_HOME
echo $ORACLE_SID
# 3. GRID_HOME所在文件系统空间充足
df -h $ORACLE_HOME
# 4. OLR文件存在且权限正确
ls -l $ORACLE_HOME/cdata/
# 5. 多路径状态正常
multipath -ll | grep -c "active ready"
这些检查只需要几分钟,但如果出了问题,可能让你在深夜多折腾两小时。尤其是ORACLE_SID配置错误的情况,asmcmd启动时找不到实例,最后报错的方式五花八门,很容易误导排查方向。
5.3 操作习惯调整与复盘心得
最后说几条从多次踩坑里总结出来的操作习惯,这几条比任何技术方案都管用。
第一,自动化脚本不要重度依赖asmcmd。能写SQL的用SQL,必须用asmcmd的用timeout包一层,并且加上重试逻辑。这样即使asmcmd抽风,脚本也能自愈。
第二,asmcmd卡死时,先把现场信息抓全再动手。进程状态、系统调用、ASM视图、系统日志,这四样数据每一样都可能决定排查方向。如果一上来就kill进程或者重启实例,等事后想复盘就会发现,关键证据已经没了。
第三,要学会区分“命令没响应”和“实例真死”。很多DBA在asmcmd卡死时第一反应是重启ASM实例,但这往往是小题大做。我自己总结的判断顺序是:先看进程状态,再看sqlplus能否连接,最后才决定要不要动实例。九成情况下,SQL能连上就说明ASM实例本身还是活的,问题只出在客户端或者IO链路上,完全不需要重启。
如果你也遇到了asmcmd卡死的情况,不妨按上面的排查顺序走一遍。也希望你能保留好事发时的日志,很多时候,那根卡住的命令背后的等待事件,才是真正告诉你根因在哪的线索。
