← ReJava · 学习目录

REJAVA / LEARNING NOTES

第13课速查:锁范围与等待处理

随时回查术语、规则和示例;完整解释见对应课程。

Java 17 / 21更新于 2026.10.12约 4 分钟阅读

根据课程材料整理

本次更新说明
  • 整理为公开学习笔记,保留要点、对照表与练习入口。

返回完整讲解

1.“B等A”省略了什么

A已经取得20号办件的X记录锁,事务还没结束。B也要修改20号,需要取得同一条记录的X锁。这两把锁不能由两个事务同时持有,所以B的更新语句要等A提交或回滚、释放锁后才有机会继续。

A的查询已经返回结果,不等于A的事务已经结束。B取得锁后,还应重新检查当前业务状态,不能直接使用等待之前的判断。

2.记录、间隙、临键分别保护什么

假设主键索引里只有10、20、30号记录。15号不存在,如果新增15号,它会插入10与20之间。下面三行是三个不同场景;没有其他锁或约束造成冲突,更新只改经办人,不改办件编号。

A持有什么锁B新增15号B修改20号
20号的X记录锁可以执行,A没有保护15的插入位置需要等A释放20号的锁
10与20之间的纯间隙锁需要等待,这个插入位置被A保护可以执行,A的纯间隙锁没有保护20号记录
20号上的X临键锁需要等待,因为临键锁包含前面的间隙需要等待,因为临键锁还包含20号记录的X锁

record lock(记录锁)保护已有索引记录。gap lock(间隙锁)阻止向指定空隙插入。next-key lock(临键锁)把某条记录与它前面的间隙一起保护。

(10,20) 表示大于10、小于20,排除两端;(10,20] 还包括20号记录。这里的间隙是索引顺序上的范围,不是预先存好的空白行。

3.哪些条件会改变锁范围

A使用主键条件 id = 20 做锁定查询,并实际找到20号记录时,只需要给这条记录加记录锁。若A改成锁定查询 id > 10,就会处理一个范围,还需要考虑阻止相应范围里的新插入。20及其前面的间隙只是这条范围查询保护范围的一部分。

如果A查询不存在的15号,即使结果为空,也仍可能锁住15所在的间隙。若SQL最后只返回一行,也不能因此断定扫描只经过一行、只锁了一行。需要结合索引、执行计划和实际锁信息判断。

READ COMMITTED(读已提交)与REPEATABLE READ(可重复读)下的普通一致性读不等于申请S记录锁。S记录锁可与别人的S记录锁同时持有;X记录锁与别人的S或X记录锁冲突。纯间隙锁之间可以共存,不套用记录锁的冲突表。

读已提交下搜索与扫描一般不使用间隙锁,但外键/重复键检查有例外,同记录上的更新冲突也仍然存在。

4.怎样从等待变成死锁

只有B等待A,而A能继续执行并提交,是普通等待。

如果A已经锁住10号,接着想改20号;B已经锁住20号,接着想改10号,两边就互相挡住了:A要等B释放20号的锁,B要等A释放10号的锁。这形成死锁。开启检测后,数据库会选择一个完整事务回滚,释放它的锁,另一方才有机会继续。

让两笔事务都先改10号、再改20号,可以避免本例的反向等待:后到者会先等10号,还没有拿住20号。但这不能保证所有其他场景都没有死锁。

5.报错后,先看哪些修改被撤销

死锁导致整个事务回滚: A第一条已经成功的修改也被撤销。若业务允许重试,A应开启新事务,重新检查状态,再完成两条修改,最后一起提交。

默认锁等待超时: 数据库通常只撤销当前超时语句,A前一步修改仍可能留在原事务中。两条必须一起成功时,应用应明确回滚整个事务,不能提交只改完一条的结果。

超时回滚配置和框架处理可能改变实际行为,应分别核对。重试次数要有限;数据库回滚不能撤销已经发出的短信等外部动作。

6.B一直等待时,先找A为什么没结束

先确认B需要哪条记录的锁、由哪个事务持有。如果A锁定读取后一直等页面确认,就把等待用户的时间移出持锁事务;真正提交修改时,再开启短事务、取得锁、重新检查当前状态并修改。必须一起成功的两条修改仍应放在同一个事务里。

拓展工具:data_lock_waits显示当前等待请求与阻挡它的锁之间的关系;data_locks提供表、索引与锁类型。最近一次已检测的死锁可看SHOW ENGINE INNODB STATUS。只看到B等待A,还不足以证明双方互相等待。

复习时怎样使用

先用自己的话说“B想做什么、A持有什么锁、B具体在等什么”。遇到说不完整的地方,回到对应例子,不必先背锁名。

依据:官方锁机制、语句设置的锁、错误处理及死锁处理。本课SQL示例尚未实测,不能把预期当成运行结果。

返回完整讲解 · 检验 · 课后问答 · 课程目录