乐观锁与悲观锁:死锁分析与解决方案

乐观锁和悲观锁

悲观锁:

  • 假设会发生冲突,因此在操作数据之前就对数据加锁,确保其他事务无法访问该数据。常见于对数据一致性要求较高的场景。

  • 实现方式 :使用行级锁或表级锁 ,例如可以使用 SELECT ... FOR UPDATEUPDATE 语句来加锁。

  • 使用场景 :悲观锁适合并发冲突多,写多读少 的场景。通过每次加锁的形式来确保数据的安全性,吞吐量较低 。但是可能死锁

sql 复制代码
-- 【读取数据并加锁 FOR UPDATE】
SELECT id, name FROM users WHERE id = 1 FOR UPDATE;
​
-- 【更新】操作
UPDATE users SET name = 'new_name' WHERE id = 1;

乐观锁:

  • 假设不会发生冲突,因此在操作数据时不加锁,而是在更新数据时进行版本控制或校验。如果发现数据被其他事务修改,则会拒绝当前事务的修改,需重新尝试。

  • 实现方式 :通常通过版本号或时间戳来实现,每次更新时检查版本号或时间戳是否一致。

  • 使用场景 :乐观锁适合并发冲突少,读多写少 的场景,不用通过加锁只需通过比较字段版本号(或时间戳)是否发生改变的形式,无 锁操作,吞吐量较高

sql 复制代码
-- 假设有一张用户表 users,包含 id、name 和 version 字段
-- 【查询】数据
SELECT id, name, version FROM users WHERE id = 1;
​
-- 【更新】数据时检查版本号
UPDATE users
SET name = 'new_name', version = version + 1
WHERE id = 1 AND version = current_version;

死锁

死锁产生

一、事务加锁顺序不一致

这是最常见的死锁原因。多个事务对不同资源的加锁顺序相反,导致循环等待。

示例:

  • 事务 A:先更新表 A 的行 1 → 再更新表 B 的行 2

  • 事务 B:先更新表 B 的行 2 → 再更新表 A 的行 1

此时事务 A 持有表 A 行 1 的锁,等待表 B 行 2 的锁;事务 B 持有表 B 行 2 的锁,等待表 A 行 1 的锁,形成死锁。

二、锁的粒度选择不当

  • 行锁升级为表锁 :如果事务操作的数据范围过大(如未命中索引的UPDATE/DELETE),InnoDB 会将行锁升级为表锁,增大锁冲突概率,进而引发死锁。

  • 间隙锁(Gap Lock)冲突:InnoDB 在可重复读隔离级别下会使用间隙锁防止幻读,若多个事务在同一间隙内插入数据,可能因间隙锁冲突导致死锁。

三、(大)事务持有锁时间过长

事务执行时间过长(如包含大量操作、等待用户输入等),会长时间持有锁,增加与其他事务的锁冲突概率,最终触发死锁。

四、并发修改相同资源的不同维度

多个事务从不同维度(如主键、唯一索引)修改同一批数据,可能因加锁顺序不同导致死锁。

示例:

  • 事务 A:按主键 ID=1 更新 → 按唯一索引 name=' 张三 ' 更新

  • 事务 B:按唯一索引 name=' 张三 ' 更新 → 按主键 ID=1 更新

五、外键约束触发的隐式锁

外键约束会导致 InnoDB 自动加锁校验关联数据,若多个事务同时操作存在外键关联的表,可能因隐式锁的顺序问题引发死锁。

六、批量操作的锁竞争

批量插入 / 更新(如INSERT ... SELECTUPDATE ... WHERE)时,若多个事务同时操作重叠的数据范围,容易因锁竞争形成循环等待。

解决死锁

自动检测与回滚:

  • MySQL自带死锁检测机制 ,当检测到死锁时,数据库会自动回滚其中一个事务,以解除死锁。通常会回滚事务中持有最少资源的那个

  • 还可以设置锁等待超时的参数,当获取锁的等待时间超过阈值时,就释放锁进行回滚。

此外还有以下操作:

  • 手动kill发生死锁的语句 :可以通过命令,手动快速地找出被阻塞的事务及其线程ID,然后手动kill它,及时释放资源。

  • 避免大事务长时间占据锁。将大事务拆分成多个小事务快速释放锁,可降低死锁产生的概率和避免冲突。

  • 调整锁的顺序,大范围修改的优先。在更新数据的时候要保证获得足够的锁,先获取影响范围大的锁;或者固定顺序也可以。

  • 更改数据库隔离级别。可重复读比读已提交多了间隙锁和临键锁,降低为读已提交级别可以降低死锁的情况。

  • 合理建立索引,减少加锁范围。如果命中索引,则会锁对应的行,避免全表扫描。

排查语句

使用 SHOW ENGINE INNODB STATUS 来获取死锁的日志信息,从而定位到死锁发生的原因。

相关推荐
光羽隹衡2 小时前
MySQL的安装
数据库·mysql
脸大是真的好~2 小时前
尚硅谷-mysql专项训练-数据库服务的优化-慢查询-EXPLAIN字段
数据库·mysql·性能优化
梦未2 小时前
Spring控制反转与依赖注入
java·后端·spring
喜欢流萤吖~2 小时前
Lambda 表达式
java
无限大62 小时前
验证码对抗史
后端
Dragon online2 小时前
数据分析师成长之路--从SQL恐惧到数据掌控者的蜕变
数据库·sql
ZouZou老师2 小时前
C++设计模式之适配器模式:以家具生产为例
java·设计模式·适配器模式
用户2190326527352 小时前
Java后端必须的Docker 部署 Redis 集群完整指南
linux·后端
曼巴UE52 小时前
UE5 C++ 动态多播
java·开发语言
VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue音乐管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计