在Oracle数据库的日常运维中,锁问题几乎是绕不开的话题。一个事务忘记提交,可能让 dozens 个业务会话集体卡死;一次DDL操作拿不到锁,可能让整个表的DML全部排队。要弄清楚是谁持有锁、谁在等锁、等的又是什么锁,最基础的入口就是v$lock视图。这个视图实时反映了当前实例中锁的持有和请求情况,掌握它的使用方法,是每一个DBA和后端开发人员排查阻塞问题的基本功。

v$lock视图的核心字段解析
v$lock的结构并不复杂,但要看懂它,必须先理解几个关键列的含义。SID标识持有或请求锁的会话编号,TYPE标识锁的类型,常见的有TM(表级锁,与DML操作相关)、TX(事务锁,事务开始且第一次修改数据时获取)、UL(用户自定义锁)等。ID1和ID2的含义随锁类型变化:对于TX锁,ID1是事务回滚段事务槽在USN中的位置信息,两个事务如果要锁同一行,它们的TX锁ID1、ID2会相同;对于TM锁,ID1则是被锁定对象的对象号,可以直接关联dba_objects查出表名。
最关键的两列是LMODE和REQUEST。LMODE表示当前会话已经持有的锁模式,取值从0到6,分别对应无锁、空锁、行共享、行独占、共享、共享行独占和独占。REQUEST表示会话正在等待获取的锁模式,如果REQUEST大于0,说明这个会话正在被阻塞,它想拿的锁被别人持有。换句话说,判断阻塞的口诀很简单:LMODE大于0是在持锁,REQUEST大于0是在等锁。一个经典的阻塞场景就是:会话A对某行执行了UPDATE但未提交,持有了TX锁的独占模式;会话B更新同一行时,会在v$lock中出现一条LMODE为0、REQUEST为6的记录,其ID1、ID2与A持有的那条TX锁完全一致。
CTIME列记录锁被持有或被请求的秒数,BLOCK列则标识这个锁是否阻塞了其他会话,取值为0表示未阻塞他人,取值为1或大于1表示正在阻塞相应数量的其他会话。BLOCK是一个非常有用的信号,配合CTIME可以快速判断阻塞的严重程度。
常用的锁监控SQL语句
最基础的查询是直接查看当前存在的锁,按类型过滤出事务锁和表锁即可。下面这条SQL能列出所有持锁和等锁的会话:
SELECT sid, type, id1, id2, lmode, request, ctime, block
FROM v$lock
WHERE type IN ('TM', 'TX')
ORDER BY sid;
单独看v$lock只能看到会话编号,实际排查时必须关联v$session才能拿到具体信息。下面这条SQL将持锁会话与等锁会话成对列出,是排查阻塞最常用的脚本之一:
SELECT lp.sid 持锁会话, ls.username 持锁用户, ls.machine 持锁机器,
ls.sql_id 持锁SQL_ID, lp.ctime 持锁时长秒,
ws.sid 等锁会话, wt.username 等锁用户, wt.machine 等锁机器
FROM v$lock lp
JOIN v$session ls ON lp.sid = ls.sid
JOIN v$lock wq ON wq.type = lp.type AND wq.id1 = lp.id1 AND wq.id2 = lp.id2
JOIN v$session wt ON wq.sid = wt.sid
WHERE lp.lmode > 0
AND wq.request > 0
AND lp.block > 0;
排查TM锁时,可以把ID1关联dba_objects,直接看到锁的是哪张表,进一步关联v$locked_object和v$sql还能看到等锁会话正在执行的SQL文本。如果需要分析阻塞链条,也就是A阻塞B、B又阻塞C的情况,可以用分层查询逐级展开,或者借助v$session的blocking_session列(Oracle 10g以后提供),它能直接告诉你当前会话被哪个SID阻塞,一条简单的查询就能看清整条等待链。
相关视图对比与实际处置建议
除了v$lock,Oracle还提供了几个辅助视图。v$locked_object视图展示了当前被锁定的对象及持有锁的会话和操作系统用户名,信息更贴近对象视角,适合快速回答“哪张表被锁了”这个问题;dba_dml_locks和dba_ddl_locks需要先执行dbms_lock包相关的CATBLOCK.SQL脚本创建,它们以更易读的方式展示DML锁和DDL锁,比如能看到具体的锁模式名称而不是数字;v$transaction则记录事务信息,配合v$lock的TX锁可以判断事务开始时间、是否长时间未提交。这些视图各有侧重,实际工作中往往组合使用:先用v$lock或v$session的blocking_session定位阻塞源,再用v$sql或v$sqlarea查看持锁会话最后执行的语句,最后用v$transaction确认事务状态。
定位到持锁会话后如何处置,需要结合业务判断。如果持锁会话是一个忘记提交的交互式工具会话,持锁时长已经很长且SQL早已执行完毕,通常可以直接与相关人员确认后kill掉;如果持锁的是一个正在正常执行的批量任务,贸然kill会导致事务回滚,回滚本身也可能很耗时。杀会话的语句是ALTER SYSTEM KILL SESSION 'sid,serial#',sid和serial#从v$session中获取。另外要注意,Oracle的死锁会被自动检测,后台进程会在跟踪文件和告警日志中记录ORA-00060错误并主动终止其中一个事务,这种情况不需要人工干预,但应该根据trace文件中的信息修正应用逻辑,避免两个事务以相反顺序访问同一批资源。
从预防角度看,锁冲突频繁的系统通常存在几个共性问题:事务过大导致锁持有时间过长、应用访问多张表时顺序不一致、前端工具查询后长时间不提交等。除了事后用v$lock排查,也可以在Oracle 11g以后利用dbms_lock.sleep配合定时任务做常态化监控,或者基于v$session的wait_class和event字段(如enq: TX - row lock contention等待事件)建立告警,做到在业务大面积阻塞之前就发现问题。
锁模式对业务影响的判断
理解锁模式的兼容性,才能准确评估锁冲突的严重程度。行级排他锁(LMODE为3)允许其他事务修改同一表的其他行,只在对同一行操作时互斥;共享锁(LMODE为4)则允许并发读取但阻止修改。TM锁的模式由DML语句类型决定:INSERT、UPDATE、DELETE产生行级排他模式,而LOCK TABLE IN SHARE MODE会产生共享模式,后者会阻塞所有DML,对业务影响大得多。看到v$lock中出现了不常见的高级别锁模式,比如SHARE或EXCLUSIVE模式的TM锁,基本可以断定是有人显式执行了LOCK TABLE或某些特殊工具行为,这类锁往往是业务突然卡死的元凶,应优先处理。
总的来说,v$lock是Oracle锁问题排查的基石视图。掌握LMODE与REQUEST的判断逻辑、TX锁与TM锁的区别、以及和v$session、dba_objects的关联方法,面对阻塞类故障时就能做到心中有数:先看谁在等、再找谁在堵、最后决定是等待、提交还是终止会话。把这些SQL整理成脚本放进运维工具箱,故障来临时可以节省大量定位时间。