记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

昨晚我正在床上睡得着着的,突然来了一条短信。

记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

啥,线上MySQL死锁了,我赶紧登录线上系统,查看业务日志。

记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

能清楚看到是这条insert语句发生了死锁。

MySQL如果检测到两个事务发生了死锁,会回滚其中一个事务,让另一个事务执行成功。很明显,我们这条insert语句被回滚了。

insert into user (id, name, age) values (6, '张三', 6);

但是我们怎么排查这个问题呢?

到底跟哪条SQL产生了死锁?

好在MySQL记录了最近一次的死锁日志,可以用命令行工具查看:

show engine innodb status;

记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

在死锁日志中,可以清楚地看到这两条insert语句产生了死锁,最终事务2被会回滚,事务1执行成功。

事务1
insert into user (id,name,age) values (5,'张三',5);
事务2
insert into user (id,name,age) values (6,'李四',6);

这两条insert语句,怎么看也不像能产生死锁,我们来还原一下事发过程。

先看一下对应的Java代码:

@Override
@Transactional(rollbackFor = Exception.class)
public void insertUser(User user) {
    User userResult = userMapper.selectByIdForUpdate(user.getId());
    // 如果userId不存在,就插入数据,否则更新
    if (userResult == null) {
        userMapper.insert(user);
    } else {
        userMapper.update(user);
    }
}

业务逻辑代码很简单,如果userId不存在,就插入数据,否则更新user对象数据。

从死锁日志中,我们看到有两条insert语句,很明显userId=5和userId=6的数据都不存在。

所以对应的SQL执行过程,可能就是这样的:

记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

先用for update加上排他锁,防止其他事务修改当前数据,然后再insert数据,最后发生了死锁,事务2被回滚。

两个事务分别在两个主键ID上面加锁,为什么会产生死锁呢?

如果看过上篇文章,就会明白。

当id=5存在这条数据时,MySQL就会加 Record Locks(记录锁),意思就是只在id=5这一条记录上加锁。

当id=5这条记录不存在时,就会锁定一个范围。

假设表中的记录是这样的:

id name age 1 王二 1 10 一灯 10

select * from user where id=5 for update;

这条select语句锁定范围就是 (1, 10]

最后两个事务的执行过程就变成了:

记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

通过这个示例看到,两个事务都可以先后锁定 (1, 10]这个范围,说明MySQL默认加的临键锁的范围是可以交叉的。

那怎么解决这个死锁问题呢?

我能想到的解决办法就是,把这两个语句select和insert,合并成一条语句:

insert into user (id,name,age) values (5,'张三',5)
    on duplicate key update name='张三',age=5;

大家有什么好办法吗?

这个死锁情况,还是挺常见的,赶紧回去翻一下项目代码有没有这样的问题。

文章持续更新,可以微信搜一搜「 一灯架构 」第一时间阅读更多技术干货。

Original: https://www.cnblogs.com/yidengjiagou/p/16425196.html
Author: 一灯架构
Title: 记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/586946/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • SpringBoot整合SpringSecurityOauth2实现鉴权-动态权限

    写在前面 思考:为什么需要鉴权呢? 系统开发好上线后,API接口会暴露在互联网上会存在一定的安全风险,例如:爬虫、恶意访问等。因此,我们需要对非开放API接口进行用户鉴权,鉴权通过…

    Java 2023年6月16日
    093
  • 设计原则之KISS,YAGNI原则

    KISS原则 kiss原则的英文描述有好几个版本,比如下面这几个。 Keep It Simple and Stupid. Keep It Short and Simple. Kee…

    Java 2023年6月7日
    073
  • Java8中Stream详细用法大全

    二、分类 无状态:指元素的处理不受之前元素的影响; 有状态:指该操作只有拿到所有元素之后才能继续下去。 非短路操作:指必须处理所有元素才能得到最终结果; 短路操作:指遇到某些符合条…

    Java 2023年5月29日
    050
  • Docker安装InfluxDB1.x和InfluxDB2.x以及与SpringBoot整合

    两者区别: 1.x 版本使用 influxQL 查询语言,2.x 和 1.8+(beta) 使用 flux 查询语法;相比V1 移除了database 和 RP,增加了bucket…

    Java 2023年6月13日
    080
  • 获取线程对象的名称

    获取线程对象的名称 1.获取当前线程对象//调用当前线程对象,currentThread()这方法出现在main()方法中,当前线程就是主线程//这代码出现在哪,就是获取到哪个线程…

    Java 2023年6月9日
    052
  • 如何高效地写 Form

    工作少不了写”增删改查”,”增删改查”中的”增”和”改”都与 Form 有关,可以说…

    Java 2023年6月16日
    050
  • Lombok 同时使用 @Data 和 @Builder 的巨坑,千万别乱用!

    来源:juejin.cn/post/7103011031672176677 问题背景 Lombok同时使用@Data和@Builder ,会出现构建无参构造器报错!最终导致编译不通…

    Java 2023年6月15日
    054
  • 修改IntelliJ IDEA字体

    posted @2016-04-05 10:11 聊聊IT那些事 阅读(254 ) 评论() 编辑 Original: https://www.cnblogs.com/FCWORL…

    Java 2023年6月6日
    092
  • Springboot原理

    1. SpringBoot特点 一个starter导入所有 依赖管理 父项目做依赖管理:声明了所需依赖的版本号 依赖管理 org.springframework.boot spri…

    Java 2023年6月7日
    066
  • pagination分页插件使用demo

    `jsp pagination插件使用 $(function() { $(“#demo_pag1”).bs_pagination({ currentPage:1,//当前页号,相当…

    Java 2023年6月9日
    047
  • linux安装elasticsearch

    Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎…

    Java 2023年6月8日
    0100
  • 从java toBinaryString() 看计算机数值存储方式(原码、反码、补码)

    1.1 方法说明 该方法位于 java.lang.Integer类中方法签名: public static String toBinaryString(int i)含义:返回参数数…

    Java 2023年6月6日
    080
  • 贪吃蛇项目的制作

    该项目是根据GUI编程学习所写,详细的学习课程可以参考狂神说老师的GUI课程:一小时开发贪吃蛇。 以下仅仅为简要说一下 逻辑,源码放在最后。 实现该项目总共用到三个类: Data数…

    Java 2023年6月7日
    075
  • maven添加oracle的依赖驱动

    1、下载需要的jdbc jar 包 2、下载jar包 3、在当前目录下输入cmd进入控制台 4、控制台输入 命令解释 5、执行后有如下就表示成功 6、在maven配置 1、下载需要…

    Java 2023年6月5日
    093
  • Spring Security登录的流程

    Spring Security登录的流程 1、UsernamePasswordAuthenticationFilter这过滤器开始 attemptAuthentication方法 …

    Java 2023年6月16日
    065
  • Java高并发教程:Java NIO简介

    Java高并发教程:Java NIO Java NIO Java NIO 全程未 Java New IO类库,目的是让Java支持非阻塞IO(Non-Block IO),故很多人也…

    Java 2023年5月29日
    065
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球