首页 > 其他分享 >记一次线上问题 → Deadlock 的分析与优化

记一次线上问题 → Deadlock 的分析与优化

时间:2023-07-31 09:23:00浏览次数:30  
标签:classStudent classStudent1 处理 add 死锁 线上 new Deadlock 优化

开心一刻

  今天女朋友很生气

  女朋友:我发现你们男的,都挺单纯的

  我:这话怎么说

  女朋友:脑袋里就只想三件事,搞钱,跟谁喝点,还有这娘们真好看

  我:你错了,其实我们男人吧,每天只合计一件事

  女朋友:啥事呀?

  我:这娘们真好看,得搞钱跟她喝点

问题复现

  需求背景

   MySQL8.0.30 ,隔离级别是默认的,也就是 REPEATABLE-READ 

  表: tbl_class_student ,id 非自增,整张表的全部字段数据都是从上游服务进行同步

  需求:上游服务发送同步MQ,本服务收到消息后再调上游服务接口,查询全量数据,对 tbl_class_student 表数据进行更新,若记录存在则更新,不存在则插入

  这需求是不是很明确?放心,没有下套!

  线上问题

  通过线上异常日志,最终定位到如下代码

  咋一看,这代码是不是无比的清晰明了?

  都不用注释,就能清楚的知道这个代码是在做什么:逐行更新,存在则更新,不存在则插入

  是不是无比的契合需求?

  但是,真的就完美无瑕吗

  且看我表演一波

  表演代码如下:

@Override
@Transactional(rollbackFor = Exception.class)
public void batchSaveOrUpdate(List<TblClassStudent> classStudents) {
    if(CollectionUtils.isEmpty(classStudents)) {
        return;
    }
    classStudents.forEach(classStudent -> {
        this.getBaseMapper().saveOrUpdate(classStudent);
        try {
            // 为了方便复现问题,睡眠1秒
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
}

// 单元测试
@Test
public void batchSaveOrUpdateTest() throws InterruptedException {

    TblClassStudent classStudent = new TblClassStudent();
    classStudent.setId(1);
    classStudent.setClassNo("20231010");
    classStudent.setStudentNo("20231010201");

    TblClassStudent classStudent1 = new TblClassStudent();
    classStudent1.setId(2);
    classStudent1.setClassNo("20231010");
    classStudent1.setStudentNo("20231010202");

    List<TblClassStudent> classStudents1 = new ArrayList<>();
    classStudents1.add(classStudent);
    classStudents1.add(classStudent1);

    List<TblClassStudent> classStudents2 = new ArrayList<>();
    classStudents2.add(classStudent1);
    classStudents2.add(classStudent);

    // 模拟2个线程,同时批量更新
    CountDownLatch latch = new CountDownLatch(2);
    new Thread(() -> {
        studentService.batchSaveOrUpdate(classStudents1);
        latch.countDown();
    }, "t1").start();
    new Thread(() -> {
        studentService.batchSaveOrUpdate(classStudents2);
        latch.countDown();
    }, "t2").start();
    latch.await();
    System.out.println("主线程执行完毕");
}
View Code

   Deadlock 就这么诞生了!

优化处理

  死锁产生条件

  死锁产生的条件,大家还记得吗?

  回到上诉案例,锁的持有、申请情况如下

  死锁自然就产生了

  那么该如何处理了

  排序处理

  不同线程调用同一个方法处理数据而产生死锁

  这种情况对处理的数据进行排序处理,使得不同线程申请数据库锁的顺序保持一致,那么就不会产生死锁

  分批处理

  事务时间越短越好

  批量逐条更新,会导致事务持续的时间很长,那么出现死锁的概率就越大

  分批处理可以减少事务时长

  加锁处理

  这里的锁指的并非数据库层面的锁,而是业务代码层面的锁

  可以是 JVM 的锁,适用于单节点部署的情况

  可以是分布式锁,适用于单节点部署,也适用于多节点部署;具体实现方式有很多,结合实际情况选择一种合适的实现方式即可

总结

  1、批量逐条更新,这是严令禁止的

    效率低下,导致事务时长大大增加,会引发一系列其他的问题

  2、数据库的加锁是比较复杂的,不同的数据库的加锁实现也是有区别的

    本篇中的死锁案例还是比较好分析的

    遇到不好分析的,需要向同事(dba、开发同事等)发出求助,也可以线上求助数据库博主

  3、面对不同问题,结合业务来分析出最合适的处理方式

    有的业务对性能要求高

    有的业务对数据准确性要求高

    

标签:classStudent,classStudent1,处理,add,死锁,线上,new,Deadlock,优化
From: https://www.cnblogs.com/youzhibing/p/17578206.html

相关文章

  • 记一次线上DB负载上升问题
    一.背景 因账目核对,目前要求是每月8日开始推送当月数据,因此需要8日推送整个1周数据(例:7月8日开始推送7月的数据)二.问题dba告知当前数据库所在集群负载增加30%以上,通过查询得知当前数据库与其他财务库两个库同属于一个集群,集群负载过大会影响其他财务结算,支付等数据部分,产生很......
  • React重新渲染的触发机制及其优化策略
    React是一个用于构建用户界面的JavaScript库,它的核心特点之一是使用虚拟DOM(VirtualDOM)来实现高效的组件渲染。那组件重新渲染的机制是如何呢?基于这些机制,如果进行优化呢?虚拟DOM是一个用JavaScript对象表示的DOM树,它可以在内存中快速地创建和修改,而不需要直接操作真实的DOM。React......
  • SQL优化汇总
    ISNOTNULL的优化1.问题提出客户系统有这样一条SQL,脱敏后如下:SELECTNVL(MAX(T1.CREATED),SYSDATE)FROMDUALLEFTJOINTEST11T1ONT1.OWNER=’OUTLN’ANDOBJECT_TYPEISNOTNULL;SQL是TEST11表和DUAL表相关联,WHERE条件中OWNER字段有索引,SQL走了该字段索引范围扫描的执行......
  • requestAnimationFrame优化动画
    requestAnimationFrame优化动画总结:requestAnimationFrame与setInterval的区别setInterval是在任务队列里执行的,也就是说上一帧没有执行完下一帧不可能执行。而requestAnimationFrame是在差异队列里执行的,也就是说没有延迟。requestAnimationFrame可以准时执行每一帧<!......
  • 【笔记】DP 优化(WIP)
    7.30DP凸相关决策单调性、斜率优化、凸、四边形不等式,都是凸相关。前置知识四边形不等式:交叉小于包含。\(l_1<l_2<r_1<r_2\tow(l_1,r_1)+w(l_2,r_2)\leqw(l_1,r_2)+w(l_2,r_1)\)。区间包含单调性:包含区间值单调。\(l_1<l_2<r_2<l_1\tow(l_1,r_1)\geqw(l_2,r_2)\)。满足......
  • 索引优化
    联合索引第一个字段如果是范围查询则不会走索引如果第一个查询条件用范围查询,那么MySQL会以为你查询的表数据比较多,那么它就会进行全表扫描而不进行索引,,但是如果第一个查询条件是主键范围查询的话,它依旧会走索引,不过在开发中应该不需要进行主键范围查询。主键范围查询EXPLAINS......
  • js代码优化
    //案例:functionpreviewWeek(i){switch(i){case1:return'星期一'break;case2:return'星期二'break;case3:return'星期三'break;......
  • Mysql数据库中数据量特别大,读取特别慢,已经做了索引,怎么优化
    当MySQL数据库中的数据量特别大,读取操作变得特别慢,即使已经添加了索引,仍然需要进一步优化。下面是一些建议以及示例代码来优化这种情况:使用合适的索引:确保为频繁查询的列添加了合适的索引。可以使用EXPLAIN语句来分析查询的执行计划,检查是否正确使用了索引。示例代码:sqlCopycodeE......
  • 性能优化之详解各种指标
    前言上篇文章最后提到了我们可以通过performance的一些属性对性能做统计,我们会发现performance对象下有非常多的属性,远不止上篇文章提到的DOMContentLoaded与Load这两个事件。或许你在浏览器控制台见过它们这些身影:DCL、LCP、FP、FCP、L这里的DCL与L就是我们上篇文章介绍的DOMConte......
  • 性能优化之window.onload
    前言最近在做一些性能优化相关的工作,相信大家在工作过程中也会遇到一些性能优化相关的场景,这对于前端开发者来讲是一项加分技能。为了我们的用户在使用我们的产品时能够有一个非常好的体验,我们需要对页面进行诊断优化。在行业中,我们的页面P90在两秒内算是达标,超过这个时间那么你就......