导读:本期聚焦于小伙伴创作的《如何在Oracle数据库中动手做锁实验来理解行锁与表锁机制》,敬请观看详情。想弄明白Oracle里update为何会堵住别的会话,亲手做锁实验比看文档直观得多。本文给出可复现的操作步骤,用两个SQL Plus会话分别对同一行加行锁、对全表加锁,借助v$lock和v$session视图观察LMODE、REQUEST字段变化。你会看到行锁本质是TX锁加在数据行指向的兴趣链上,表锁则是TM锁保护结构。实验还覆盖死锁触发与自动回滚现象,帮助厘清阻塞链条,不再凭想象判断并发冲突。

Oracle数据库的锁机制是并发控制的核心,很多人在写代码时只知道“更新会加锁”,却不清楚锁到底落在哪、不同语句产生什么类型的锁、会话之间如何相互阻塞。通过搭建简单的锁实验环境,我们可以用最朴素的SQL会话观察真实行为。

如何在Oracle数据库中动手做锁实验来理解行锁与表锁机制

一、实验环境准备

做锁实验不需要复杂架构,本机安装Oracle 11g或以上版本即可。我们打开两个独立的SQL Plus窗口,分别称之为会话A和会话B。两个会话使用同一账号连接同一实例,这样能直接看到彼此的锁冲突。

先创建一张极简的测试表,并插入几条数据,避免实验受业务表干扰。建表语句如下,注意commit让数据真正落盘:

create table lock_demo (
  id   number primary key,
  name varchar2(20)
);
insert into lock_demo values (1, 'a');
insert into lock_demo values (2, 'b');
commit;

此时表中存在两行记录。我们需要记住主键值,后续实验会针对id=1这一行做文章。建议两个会话都执行set linesize 200col column format a20,方便观察宽字段输出。

二、行锁实验:观察TX锁与阻塞

在会话A中执行一条更新但不提交,Oracle会在被修改行上放置行级排他锁,同时在表上放置行共享级TM锁。行锁在Oracle内部以TX锁表示,它并不存储在行头,而是通过事务槽和兴趣链定位。

-- 会话A:更新id=1,不提交
update lock_demo set name = 'a1' where id = 1;

切换到会话B,尝试更新同一行。此时会话B会立即挂起,因为会话A的TX锁尚未释放。这说明Oracle的行锁是排他的,同一行只能有一个事务修改。

-- 会话B:同样更新id=1,会被阻塞
update lock_demo set name = 'a2' where id = 1;

为了看清锁状态,我们在第三个会话或会话B超时前用DBA视图查询。v$lock中的LMODE表示已持有模式,REQUEST表示请求模式,0代表无,6代表排他。

select s.sid, l.type, l.lmode, l.request, l.block
from v$lock l, v$session s
where l.sid = s.sid
and s.username = 'YOUR_USER';

查询结果中,会话A对应TX锁LMODE=6,TM锁LMODE=3;会话B对应TX锁REQUEST=6,说明它在等待排他行锁。block字段为1表示会话A阻塞了别人。这个实验直观展示了行锁的本质:锁信息在事务层,行只是被事务标记。

三、表锁实验:TM锁的级别差异

除了行锁,Oracle的DML还会加表级TM锁。不同操作加的TM锁模式不同,比如select for update加RS锁(2级),普通update加RX锁(3级),lock table加S或X锁。我们用lock命令模拟表级排斥。

-- 会话A:显式锁全表,排他模式
lock table lock_demo in exclusive mode;

这时会话A持有TM锁LMODE=6。会话B再执行任何DML都会阻塞,因为RX(3级)与X(6级)不兼容。我们可以在会话B试一条insert:

-- 会话B:被表锁阻塞
insert into lock_demo values (3, 'c');

通过对比行锁与表锁实验,能发现表锁保护的是对象结构而非数据行。应用层如果滥用lock table,会让并发骤降。正常OLTP系统应依赖行锁而非表锁。

锁类型视图中的TYPE常见模式冲突示例
行排他锁TXLMODE=6同行另一TX
表锁TMRX(3)/X(6)X阻塞RX

四、死锁实验与Oracle处理

死锁是两个会话互相等待对方持有的锁。我们构造场景:会话A改id=1,会话B改id=2,然后互相改对方已锁的行。

-- 会话A
update lock_demo set name='x' where id=1;
-- 会话B
update lock_demo set name='y' where id=2;
-- 会话A再执行(被B阻塞)
update lock_demo set name='x' where id=2;
-- 会话B再执行(被A阻塞,形成死锁)
update lock_demo set name='y' where id=1;

Oracle内置死锁检测线程会识破这种循环等待,通常一秒内抛出自带错误并回滚其中一个会话的当前语句,另一个得以继续。这与我们手写超时逻辑不同,是数据库兜底机制。

观察alert日志或会话报错能看到ORA-00060。实验证明,死锁不是必须避免的灾难,但应在代码里缩小事务范围,减少交叉更新来降低概率。

五、实验总结与进阶

亲手做Oracle锁实验能破除很多误解,比如“锁在行上”其实只是逻辑说法,物理上锁属于事务。借助v$lock能定位线上阻塞源,结合v$session的blocking_session字段可快速找到罪魁。

进阶可以尝试在PL/SQL里用dbms_lock包自定义锁,或开启trace看更底层Enqueue。掌握这些,调优并发与排查hang问题就有了扎实根基。

Oracle锁行锁表锁修改时间:2026-08-03 09:03:29

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。