gpt4 book ai didi

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

转载 作者:我是一只小鸟 更新时间:2023-07-31 14:32:09 25 4
gpt4 key购买 nike

开心一刻

  今天女朋友很生气 。

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

  我:这话怎么说 。

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

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

  女朋友:啥事呀?

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

问题复现

  需求背景

    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、面对不同问题,结合业务来分析出最合适的处理方式 。

    有的业务对性能要求高 。

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

最后此篇关于记一次线上问题→Deadlock的分析与优化的文章就讲到这里了,如果你想了解更多关于记一次线上问题→Deadlock的分析与优化的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com