Oracle数据库快速定位锁表原因存储过程怎么写?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1438个文字,预计阅读时间需要6分钟。
相关专题内容如下:
查 v$locked_object 确认哪些表真被锁了
别一上来就杀会话,先看是不是真锁了。直接查 v$locked_object 最快,它只返回当前正持有 dml 锁的对象(比如 insert/update/delete 未提交导致的行级锁升级或阻塞)。注意这个视图不包含 select for update 的轻量锁,也不反映 ddl 锁(如 alter table),所以看到空结果 ≠ 没锁。
常用组合查询:
SELECT b.owner, b.object_name, a.session_id, a.locked_mode FROM v$locked_object a, dba_objects b WHERE a.object_id = b.object_id;
如果 object_name 是你关心的表名,说明它正在被某个 SID 占着;locked_mode 值为 3(Row-X)或 6(Exclusive)通常意味着写操作没提交。
- 必须有
SELECT ANY DICTIONARY或SELECT_CATALOG_ROLE权限,否则报 ORA-00942 -
dba_objects要求用户有 DBA 角色或对目标 schema 有访问权,否则换用all_objects(但可能漏掉其他用户的表) - 该视图不保留历史,只反映“此刻正在锁”的状态,瞬时锁(如快速提交的事务)容易错过
连查 v$session 和 v$sql 定位谁在跑什么 SQL
光知道 SID 不够,得知道它在执行什么、连的哪台机器、用了什么程序。关键是要把 v$locked_object 的 session_id 关联到 v$session 的 sid,再通过 sql_id 或 prev_sql_addr 拿到实际语句。
推荐这条链路(兼容 Oracle 11g–19c):
SELECT s.sid, s.serial#, s.username, s.machine, s.program, s.logon_time, q.sql_text FROM v$locked_object l JOIN v$session s ON l.session_id = s.sid LEFT JOIN v$sql q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE' OR s.sql_id IS NOT NULL;
重点看 sql_text 字段:如果是一条长事务里的 UPDATE,且没 COMMIT,基本就是根因;如果是 PL/SQL 块开头的 BEGIN ... END;,说明锁来自存储过程内部逻辑。
-
v$sql只缓存已硬解析的 SQL,若语句刚执行完就被刷出共享池,sql_text会为空,此时得靠q.sql_fulltext(需 12c+)或查v$session的prev_sql_addr+v$sqlarea -
machine和program能快速判断是应用服务器、PL/SQL Developer 还是某定时任务进程 - 如果
username是NULL,说明是后台进程(如 job queue slave),要小心处理
用存储过程批量查锁并生成 kill 语句(不自动执行)
手动拼 ALTER SYSTEM KILL SESSION 'sid,serial#' 很累,但直接让存储过程帮你 生成可执行语句 是安全又高效的折中方案。不要用 “自动循环 kill” 的存储过程——万一误杀前台业务会话,后果比锁表还严重。
下面这个函数只输出 kill 命令列表,贴过去前还能人工核对:
SELECT 'ALTER SYSTEM KILL SESSION ''' || s.sid || ',' || s.serial# || ''' IMMEDIATE;' AS kill_cmd, s.username, s.machine, o.object_name, s.logon_time FROM v$locked_object l JOIN dba_objects o ON l.object_id = o.object_id JOIN v$session s ON l.session_id = s.sid WHERE o.object_name IN ('YOUR_TABLE_NAME');
把 YOUR_TABLE_NAME 换成真实表名,运行后得到一串带注释的 kill 命令,复制粘贴前扫一眼 username 和 machine 是否合理。
- 加
IMMEDIATE是为了绕过等待队列,立刻中断会话,避免KILL SESSION自己也被锁住 - 如果目标表被多个 SID 锁定,结果会返回多行,别漏掉任何一行
- 该语句本身不修改数据,无权限风险,普通开发账号只要能查字典视图就能跑
为什么不能只依赖 v$lock 查锁表原因
v$lock 看起来更底层,但它展示的是所有锁类型(包括 TX、TM、UL、DX 等),其中 TM(DML enqueues)才对应表级行为,TX(transaction)只是事务锁,不指向具体对象。新手常犯的错是看到一堆 TX 行就以为是“锁表”,其实那只是两个会话在争同一个回滚段,和表无关。
典型误导查询:
SELECT sid, type, id1, id2, lmode FROM v$lock WHERE type = 'TX';
这种结果里 id1 是 rollback segment#,id2 是 slot#,完全看不出哪张表被锁。真想定位,必须走 v$locked_object → v$session → v$sql 这条路径。
-
v$lock中type = 'TM'才值得细看,此时id1是 object_id,可关联dba_objects - DBA 常用
DBA_BLOCKERS和DBA_WAITERS查死锁,但这两个视图只在发生真正死锁(ORA-00060)时才有记录,平时为空 - Oracle 12c 后引入
v$session_blockers,比老视图更实时,但依然不如v$locked_object直观
最易被忽略的一点:锁可能不在表本身,而在其索引、约束触发器或物化视图日志上。查 v$locked_object 时如果没看到目标表,试试查它的索引名(dba_indexes)、主键约束名(dba_constraints),或者执行 SELECT * FROM v$access WHERE object = 'YOUR_PROC_NAME' 看存储过程是否被其他会话调用中而持锁。
本文共计1438个文字,预计阅读时间需要6分钟。
相关专题内容如下:
查 v$locked_object 确认哪些表真被锁了
别一上来就杀会话,先看是不是真锁了。直接查 v$locked_object 最快,它只返回当前正持有 dml 锁的对象(比如 insert/update/delete 未提交导致的行级锁升级或阻塞)。注意这个视图不包含 select for update 的轻量锁,也不反映 ddl 锁(如 alter table),所以看到空结果 ≠ 没锁。
常用组合查询:
SELECT b.owner, b.object_name, a.session_id, a.locked_mode FROM v$locked_object a, dba_objects b WHERE a.object_id = b.object_id;
如果 object_name 是你关心的表名,说明它正在被某个 SID 占着;locked_mode 值为 3(Row-X)或 6(Exclusive)通常意味着写操作没提交。
- 必须有
SELECT ANY DICTIONARY或SELECT_CATALOG_ROLE权限,否则报 ORA-00942 -
dba_objects要求用户有 DBA 角色或对目标 schema 有访问权,否则换用all_objects(但可能漏掉其他用户的表) - 该视图不保留历史,只反映“此刻正在锁”的状态,瞬时锁(如快速提交的事务)容易错过
连查 v$session 和 v$sql 定位谁在跑什么 SQL
光知道 SID 不够,得知道它在执行什么、连的哪台机器、用了什么程序。关键是要把 v$locked_object 的 session_id 关联到 v$session 的 sid,再通过 sql_id 或 prev_sql_addr 拿到实际语句。
推荐这条链路(兼容 Oracle 11g–19c):
SELECT s.sid, s.serial#, s.username, s.machine, s.program, s.logon_time, q.sql_text FROM v$locked_object l JOIN v$session s ON l.session_id = s.sid LEFT JOIN v$sql q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE' OR s.sql_id IS NOT NULL;
重点看 sql_text 字段:如果是一条长事务里的 UPDATE,且没 COMMIT,基本就是根因;如果是 PL/SQL 块开头的 BEGIN ... END;,说明锁来自存储过程内部逻辑。
-
v$sql只缓存已硬解析的 SQL,若语句刚执行完就被刷出共享池,sql_text会为空,此时得靠q.sql_fulltext(需 12c+)或查v$session的prev_sql_addr+v$sqlarea -
machine和program能快速判断是应用服务器、PL/SQL Developer 还是某定时任务进程 - 如果
username是NULL,说明是后台进程(如 job queue slave),要小心处理
用存储过程批量查锁并生成 kill 语句(不自动执行)
手动拼 ALTER SYSTEM KILL SESSION 'sid,serial#' 很累,但直接让存储过程帮你 生成可执行语句 是安全又高效的折中方案。不要用 “自动循环 kill” 的存储过程——万一误杀前台业务会话,后果比锁表还严重。
下面这个函数只输出 kill 命令列表,贴过去前还能人工核对:
SELECT 'ALTER SYSTEM KILL SESSION ''' || s.sid || ',' || s.serial# || ''' IMMEDIATE;' AS kill_cmd, s.username, s.machine, o.object_name, s.logon_time FROM v$locked_object l JOIN dba_objects o ON l.object_id = o.object_id JOIN v$session s ON l.session_id = s.sid WHERE o.object_name IN ('YOUR_TABLE_NAME');
把 YOUR_TABLE_NAME 换成真实表名,运行后得到一串带注释的 kill 命令,复制粘贴前扫一眼 username 和 machine 是否合理。
- 加
IMMEDIATE是为了绕过等待队列,立刻中断会话,避免KILL SESSION自己也被锁住 - 如果目标表被多个 SID 锁定,结果会返回多行,别漏掉任何一行
- 该语句本身不修改数据,无权限风险,普通开发账号只要能查字典视图就能跑
为什么不能只依赖 v$lock 查锁表原因
v$lock 看起来更底层,但它展示的是所有锁类型(包括 TX、TM、UL、DX 等),其中 TM(DML enqueues)才对应表级行为,TX(transaction)只是事务锁,不指向具体对象。新手常犯的错是看到一堆 TX 行就以为是“锁表”,其实那只是两个会话在争同一个回滚段,和表无关。
典型误导查询:
SELECT sid, type, id1, id2, lmode FROM v$lock WHERE type = 'TX';
这种结果里 id1 是 rollback segment#,id2 是 slot#,完全看不出哪张表被锁。真想定位,必须走 v$locked_object → v$session → v$sql 这条路径。
-
v$lock中type = 'TM'才值得细看,此时id1是 object_id,可关联dba_objects - DBA 常用
DBA_BLOCKERS和DBA_WAITERS查死锁,但这两个视图只在发生真正死锁(ORA-00060)时才有记录,平时为空 - Oracle 12c 后引入
v$session_blockers,比老视图更实时,但依然不如v$locked_object直观
最易被忽略的一点:锁可能不在表本身,而在其索引、约束触发器或物化视图日志上。查 v$locked_object 时如果没看到目标表,试试查它的索引名(dba_indexes)、主键约束名(dba_constraints),或者执行 SELECT * FROM v$access WHERE object = 'YOUR_PROC_NAME' 看存储过程是否被其他会话调用中而持锁。

