记录一次mysql锁超时问题

记录一次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 锁定的索引区间内,则会导致本次插入失败(获取锁超时失败)

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值