🔗 接上一篇《Oracle健康体检指南》,今天我们深入数据库的"交通系统"------锁机制,解决会话阻塞和死锁问题。
你是否遇到过:
- 操作突然卡住,无响应?
- 应用提示"资源忙"?
- 某些事务长时间不提交?
这些问题,很可能是因为锁等待。Oracle的锁机制保证了数据一致性,但不当的使用会导致性能瓶颈。今天,我就教你如何快速定位和解决锁问题。
🧠 锁机制基础
Oracle主要锁类型:
- TX锁:事务锁(行级锁)
- TM锁:表级锁
- DDL锁:数据定义语言锁
⚠️ 关键概念:
- 阻塞:会话A持有锁,会话B等待
- 死锁:A等B,B等A → Oracle自动检测并终止一个会话
1️⃣ 实时会话监控
-- 查看所有非后台会话
SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.osuser,
s.machine,
s.program,
s.module,
s.action,
s.client_info,
s.logon_time,
ROUND(s.last_call_et / 60, 2) AS last_call_min,
s.state AS wait_state,
s.event,
s.wait_class,
s.seconds_in_wait,
s.sql_id,
s.prev_sql_id,
s.row_wait_obj# AS row_wait_object_id,
s.row_wait_file# AS row_wait_file,
s.row_wait_block# AS row_wait_block,
s.blocking_session_status,
s.blocking_instance,
s.blocking_session,
s.paddr
FROM
v$session s
WHERE
type != 'BACKGROUND';
✅ 关键字段解读:
blocking_session:被谁阻塞event:当前等待事件seconds_in_wait:等待时长row_wait_*:等待的具体行位置
2️⃣ 锁阻塞链分析
-- 锁阻塞关系树
WITH blocking_tree AS (
SELECT
'alter system kill session ''' || s.sid || ',' || s.serial# || ',@' || s.inst_id || ''' immediate;' AS kill_command,
SYS_CONNECT_BY_PATH(s.sid || '@' || s.inst_id, ' ← ') AS blocking_path,
s.inst_id,
s.sid,
s.serial#,
s.username,
s.osuser,
s.machine,
s.program,
s.status,
s.sql_id,
s.event,
s.blocking_session,
s.blocking_instance,
CONNECT_BY_ISLEAF AS is_blocked_end,
LEVEL AS blocking_level,
CASE WHEN lo.xidusn IS NOT NULL THEN 'YES' ELSE 'NO' END AS holds_locked_object
FROM
gv$session s
LEFT JOIN gv$locked_object lo ON s.sid = lo.session_id AND s.inst_id = lo.inst_id
WHERE
s.blocking_session IS NOT NULL
CONNECT BY
(s.sid || '@' || s.inst_id) = PRIOR (s.blocking_session || '@' || s.blocking_instance)
START WITH
s.blocking_session IS NOT NULL
),
sql_texts AS (
SELECT DISTINCT sql_id, sql_text
FROM gv$sql
WHERE sql_id IN (SELECT sql_id FROM blocking_tree WHERE sql_id IS NOT NULL)
)
SELECT
bt.kill_command,
bt.blocking_path,
bt.inst_id,
bt.sid,
bt.serial#,
bt.username,
bt.osuser,
bt.machine,
bt.program,
bt.status,
bt.event,
bt.blocking_level,
bt.holds_locked_object,
st.sql_text
FROM
blocking_tree bt
LEFT JOIN sql_texts st ON bt.sql_id = st.sql_id
ORDER BY
bt.blocking_level DESC, bt.sid;
✅ 解读:
blocking_path:显示阻塞链(被谁阻塞)kill_command:终止会话的命令holds_locked_object:是否持有锁对象
3️⃣ 锁定对象查询
-- 被锁定的对象
SELECT
do.object_name,
lo.session_id,
lo.inst_id,
lo.oracle_username,
lo.os_user_name,
lo.process,
lo.locked_mode
FROM
gv$locked_object lo
JOIN dba_objects do ON lo.object_id = do.object_id
ORDER BY
lo.inst_id, lo.session_id;
✅ 锁定模式:
1:NULL2:行共享(RS)3:行排他(RX)4:共享(S)5:S/Row-X6:排他(X)
🔍 重点关注:locked_mode = 6(排他锁)
4️⃣ 长时间事务检测
-- 运行超过5分钟的事务
SELECT
s.inst_id,
s.sid,
s.serial#,
s.username,
s.status,
s.machine,
s.program,
t.start_time,
ROUND(SYSDATE - t.start_time, 2) * 24 * 60 AS duration_minutes,
s.sql_id,
sq.sql_text
FROM
gv$transaction t
JOIN gv$session s ON t.addr = s.taddr
LEFT JOIN gv$sql sq ON s.sql_id = sq.sql_id AND sq.inst_id = s.inst_id
WHERE
(SYSDATE - t.start_time) > INTERVAL '5' MINUTE
ORDER BY
duration_minutes DESC;
🚨 风险:
- 长时间事务会:
- 持有锁不释放
- 阻塞其他会话
- 增加UNDO空间使用
✅ 锁问题处理策略
| 场景 | 处理方案 |
|---|---|
| 普通阻塞 | 等待或联系用户提交/回滚 |
| 关键业务阻塞 | 使用 kill_command 终止会话 |
| 死锁 | Oracle自动处理 |
| 长时间事务 | 联系应用负责人 |
⚠️ 终止会话警告:
ALTER SYSTEM KILL SESSION 'sid,serial#,@inst_id' IMMEDIATE;
- 会回滚未提交的事务
- 可能影响应用一致性
📣 总结
锁问题诊断四步法:
- 🕵️♂️ 用
v$session发现阻塞会话 - 🔗 用阻塞链分析阻塞关系
- 🔍 查询被锁定的对象
- ⏱️ 检测长时间运行的事务
🔗 下期预告:
下一篇《Oracle性能瓶颈定位》,我们将通过AWR报告和SQL监控,定位慢查询和性能热点。
📌 点赞 + 收藏,告别会话阻塞!
👉 让你的数据库畅通无阻!