当前位置:主页 > 行业资讯 > > 正文

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

时间: 2023-07-31 09:27:10 来源: 博客园
开心一刻

今天女朋友很生气


(相关资料图)

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

我:这话怎么说

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

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

女朋友:啥事呀?

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

问题复现  需求背景

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

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

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

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

线上问题

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

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

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

是不是无比的契合需求?

但是,真的就完美无瑕吗

且看我表演一波

表演代码如下:

@Override@Transactional(rollbackFor = Exception.class)public void batchSaveOrUpdate(List classStudents) {    if(CollectionUtils.isEmpty(classStudents)) {        return;    }    classStudents.forEach(classStudent -> {        this.getBaseMapper().saveOrUpdate(classStudent);        try {            // 为了方便复现问题,睡眠1秒            TimeUnit.SECONDS.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();        }    });}// 单元测试@Testpublic 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 classStudents1 = new ArrayList<>();    classStudents1.add(classStudent);    classStudents1.add(classStudent1);    List 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、面对不同问题,结合业务来分析出最合适的处理方式

有的业务对性能要求高

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

关键词:

相关文章

摩洛哥地震遇难人数已升至632人,还有329人受伤

摩洛哥强震更新:据摩洛哥媒体报道,摩洛哥地震遇难人数已升至632人,

来源:北京日报2023-09-09

湖南宁远县:深植项目建设“清廉”根

“清廉项目建设让我们切切实实感受到了县委县政府在为企业保驾护航,心

来源:腾讯网2023-09-09

封闭施工!桂林交警发布重要提示→

关于僚田互通施工期间机场高速桂林往南宁、柳州方向交通组织调整的温馨

来源:桂林生活网2023-09-09

武汉轮渡最新公告

武汉轮渡最新公告

来源:凤凰网2023-09-09

这些个人、企业税费减免!最新发布

9月7日,国务院新闻办公室举行国务院政策例行吹风会,财政部、国家税务

来源:百度新闻2023-09-09