记录一次mysql锁超时问题

在高并发环境下,由于MySQL间隙锁的存在,即使对单行记录进行操作也可能导致锁等待超时。本文详细分析了一次在压力测试中遇到的锁超时问题,通过调整事务处理顺序和深入理解间隙锁机制,最终解决了这一难题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记录一次mysql锁超时问题

问题

最近在做压力测试,测试人员发现一个问题,高并发下生成订单和更新订单的操作很多失败了,抛出如下异常;org.springframework.dao.CannotAcquireLockException: / ### Error updating database. Cause: java.sql.SQLException: Lock wait timeout exceeded,看到问题后,第一反应是,都是单行操作呀,怎么会有事务锁超时问题?自己拿到完整日志分析并结合代码找到了初步原因,业务中有个操作使用了先更新数据库,在请求接口调用的方式来实现。讲数据库操作和接口调用绑定到一个事务中了,该场景开启事务后先更新了数据库,然后去请求第三方,请求第三方超时了(60秒),mysql 默认情况下事务锁为50秒超时,因此在日志中最先看到的是org.springframework.dao.CannotAcquireLockException 异常,然后才出现网络异常日志。分析到这里基本确定问题所在,但是发现另个场景:先请求第三方接口 ,请求成功后再保存数据库,也出现了org.springframework.dao.CannotAcquireLockException 异常。到这里问题就是转变为两者虽然操作同一张表,但是都是单行操作?为什么会互相影响到呢?

问题解决

为解决问题考虑,先调整了代码的业务实现,修改了事务的先后顺序,统一修改为先调用接口,在更新本地库。修改后压测在也没有出现锁等待异常,问题初步解决

根因解决

  问题解决了,但是根因并没有找到,update  记录时都使用了索引呀,记录都是唯一的,应该使用行锁呀,怎么会互相影响呢?通过在网上查询资料,也阅读了不少相关的博客,终于找到问题根因:mysql 间隙锁。

关于间隙锁的资料网上很多,这里主要说明一点:因为update 操作时使用的索引不是唯一索引,因此行锁采用的是间隙锁,那么这里锁定的其实是索引的一小段范围,insert 操作时,如果插入的数据索引刚好落到被update 锁定的索引区间内,则会导致本次插入失败(获取锁超时失败)

### MySQL 机制详解 MySQL 中的机制是为了保障数据库在高并发环境下的数据一致性和稳定性而设计的重要功能之一。以下是关于 MySQL 机制的具体解析: #### 1. 的分类 MySQL 主要分为两种类型的:表级和行级。 - **表级 (Table-Level Lock)** 表级是最简单的定策略,适用于 MyISAM 存储引擎。它会在整个表上施加,无论是读操作还是写操作都会影响到整张表。对于 `SELECT` 操作,MyISAM 会自动给涉及的所有表加上读;而对于 `UPDATE`, `INSERT`, 和 `DELETE` 则会自动加上写[^3]。 - **行级 (Row-Level Lock)** 行级由 InnoDB 存储引擎实现,提供更高的并发能力。只有涉及到具体记录的操作才会触发行级。例如,在执行 `SELECT ... FOR UPDATE` 或者 `SELECT ... LOCK IN SHARE MODE` 时,InnoDB 只会对符合条件的特定行加而不是封整个表[^2]。 #### 2. 不同存储引擎的特性 不同的存储引擎有不同的行为: - **MyISAM**: 默认使用的是表级别的,这意味着即使是一个小范围内的更新也会阻塞其他线程对该表任何部分的访问[^3]。 - **InnoDB**: 支持事务以及更细粒度的行级,这使得它可以更好地处理复杂的多用户场景下的并发请求[^4]。 #### 3. 常见类型及其作用 除了基本的表级与行级区分外,还有几种具体的形式用于满足不同需求: - **共享 (Shared Lock, S-Lock)** 当一个客户端通过命令如 `LOCK TABLES table_name READ` 获取了一个表上的共享之后,其它客户也可以获得该表上的共享,但不能再获取排他直到当前持有共享的所有连接释放它们为止[^2]。 - **排他 (Exclusive Lock, X-Lock)** 排他允许独占式的修改权限。如果某个事务已经获得了某条记录或者某些列上的排他,则在此期间不允许别的事务再对此同一组资源申请任何形式的新——既包括另外的排他也包括新的共享[^2]。 - **GAP ** 这种特殊的用来阻止新纪录插入到现有两条连续记录之间的空隙(gap)里去。比如当我们运行下面这条SQL语句的时候就会用到gap lock:`SELECT * FROM table_name WHERE id > 10 FOR UPDATE;` - **Next-Key Lock** 是一种组合了索引记录本身(record)和其前面那个区间(gap)两者一起保护起来的一种复合型模式。主要用于解决可重复读(repeatable read)隔离级别下可能出现的幻影问题(phantom problem)[^4]。 #### 4. 死现象及预防措施 死是指两个或更多事务相互等待对方持有的资源从而进入僵局的状态。为避免这种情况发生可以采取以下方法: - 尽量按照固定的顺序访问资源; - 减少单次事务持续时间; - 设置合理的超时参数让系统能够及时发现并终止潜在的死循环状况等等[^4]。 ```sql -- 示例:如何手动解锁表 UNLOCK TABLES; ``` --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值