| 记录锁也就是仅仅把一条记录锁上,官方的类型名称为: LOCK_REC_NOT_GAP 。比如我们把id值为8的那条记录加一个记录锁的示意图如图所示。 |
| 仅仅是锁住了id值为8的记录,对周围的数据没有影响。 |

| 记录锁是有S锁和X锁之分的,称之为 S型记录锁 和 X型记录锁 。 |
| 当一个事务获取了一条记录的S型记录锁后,其他事务也可以继续获取该记录的S型记录锁,但不可以继续获取X型记录锁; |
| 当一个事务获取了一条记录的X型记录锁后,其他事务既不可以继续获取该记录的S型记录锁,也不可以继续获取X型记录锁 |
| |
| |
| |
| begin; |
| |
| update student set name = "chen" where id = 1; |
| |
| |
| |
| begin; |
| |
| select * from student where id = 3 lock in share mode; |
| |
| select * from student where id = 1 lock in share mode; |
| |
| |
| update student set name = "li" where id = 3; |
| |
| |
| update student set name = "xing" where id = 1; |
| |
| |
| commit; |
| |
| |
| update student set name = "xing" where id = 1; |
页级锁:间隙锁(Gap Locks)
:指定2行中间不允许插入数据
| MySQL 在 REPEATABLE READ 隔离级别下是可以解决幻读问题的,解决方案有两种,可以使用 MVCC 方案解决,也可以采用 加锁 方案解决。但是在使用加锁方案解决时有个大问题, |
| 就是事务在第一次执行读取操作时,那些幻影记录尚不存在,我们无法给这些 幻影记录 加上 记录锁 。InnoDB提出了一种称之为Gap Locks 的锁,官方的类型名称为: LOCK_GAP |

| # 案例1 |
| # 开启1个新的连接,开启1个事务 |
| begin; |
| # 为某个不存在的行添加1个共享锁 |
| select * from student where id= 5 lock in share mode; |
| |
| # 开启第2个新的连接,开启1个事务 |
| begin; |
| # 为同一行添加1个排他锁,按道理说不能添加成功,针对同一行记录添加共享锁和排他锁是不兼容的 |
| # 但如果这一行记录是不存在的,则可以同时添加共享锁和排他锁 |
| # 这一行不存在的记录存在与某2行记录的中间,这时添加的这个锁也成为间隙锁 |
| # 添加1个排他锁,添加成功 |
| select * from student where id = 5 for update; |
| |
| # 在间隙锁还未释放之前,向这个间隙插入数据,会插入失败,会处于阻塞状态,这就是间隙锁的作用 |
| insert into student(id, name, class) value(6, "gou", "man"); |
| |
| # 案例2 |
| # 开启1个新的连接,开启1个事务 |
| begin; |
| # 由于最大的数是20,如果在20到无穷大的这个区间开启1个间隙锁,则20到无穷大这个区间将被间隙锁锁定,不允许插入数据 |
| select * from student where id = 25 lock in share mode; |
| |
| # 开启第2个连接,开启1个事务 |
| begin; |
| # 向20到无穷大的区间插入数据,插入失败 |
| insert into student(id, name, class) value(21, "gou", "man"); |
| insert into student(id, name, class) value(26, "gou", "man"); |
| |
| # 向20之前的区间插入数据,插入成功 |
| insert into student(id, name, class) value(17, "gou", "man"); |
| # 开启第1个新的连接,开启1个事务 |
| begin; |
| # 开启1个间隙锁 |
| select * from student where id = 5 lock in share mode; |
| |
| # 开启第2个连接,开启1个新的事务 |
| begin; |
| # 开启1个相同的间隙锁 |
| select * from student where id = 5 for update; |
| |
| # 第2个连接中向间隙中插入1条记录,会处于阻塞状态 |
| insert into student(id, name, class) value(7, "aaa", "giao"); |
| |
| # 切换到第1个连接的页面,向间隙中再次插入1条记录,这时会直接报死锁的错误 |
| insert into student(id, name, class) value(6, "bbb", "giao"); |
| |
| # 死锁的原因:第1个间隙锁不允许第2个连接中的插入操作执行,第2个间隙锁不允许第1个连接中的插入操作执行 |
| # 要想第1个连接中的插入操作执行,第2个连接释放锁,但第2个连接中有1条插入语句处于阻塞状态,不能结束 |
| # 要想第2个连接中的插入操作执行,第1个连接释放锁,但第1个连接中有1条插入语句处于阻塞状态,不能结束 |
| # 最后第1个连接中的插入语句报死锁错误,第2个连接中的插入语句则插入成功了 |
| |
| # 第2个连接中插入语句执行成功的原因:发生死锁时,mysql的处理死锁的1中策略是将某1个处于僵持状态的事务进行回滚,选择成本更低的1个事务执行 |
| # 因此这里回滚了第1个连接中的事务,第2个连接没有阻塞则执行成功了 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术