• 一次生产死锁问题的处理


    一次生产死锁问题的处理

    场景

    存储制品库maven包,有两张表meta_maven存储groupId、artifactId、version信息,meta_maven_file存储GAV下的具体文件jar、pom、sha1、meta_maven.xml。

    场景1: deploy上传snapshot的maven包,用新的meta_maven.xml替换之前的,之前的删除。

    场景2: 下载GAV包,检查是否有漏洞:开始更新组件状态为扫描中,从黑鸭子获取扫描结果(超长时间等待),更新组件的扫描结果,有漏洞禁止下载。

    场景1伪代码:

    @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITED, rollbackfor=Exception.class)
    public void upload(MetaMavenFile metaMavenFile) {
       MetaMaven metaMaven = metaMavenMapper.select(metaMavenFile);
    	
       Result uploadRet = metaMavenFileService.upload(metaMavenFile);	
    	
      metaMaven.setModify("xxx");
      metaMavenMapper.update(metaMaven);	
    }
    
    @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITED, timeout = 8, rollbackfor=Exception.class)
    public Result upload(MetaMavenFile metaMavenFile) {
     MetaMavenFile existMetaMavenFile = metaMavenFileMapper.select(metamavenFile);
     String filePath = fastdfsServer.upload(metaMavenFile);
     existMetaMavenFile.setStorageReference(filePath);
     metaMavenFileMapper.updateByPrimaryKeySelective(existMetaMavenFile);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    场景2伪代码:

    
    @Transactional(propagation= Propagation.REQUIRED, isolation= Isolation.READ_COMMITED, timeout = 8, rollbackFor = Exception.class)
    public void download(MetaMavenFile metaMavenFile, HttpResponse response){
    	mataMaven existMetaMaven = metaMavenMapper.select(metaMavenFile);
    mataMavenMapper.updateCompoentStatus(existMetaMaven.getId(), ComponentStatus.SCANNING, null);
    	hasSecurityExist(existMetaMaven.getId());
    }
    
    public void hasSecurityExist(Long metaMavenId) {
     	JSONObject scanResult = getblackDuckScanResult(metaMavenId);
     	mataMaven existMetaMaven = metaMavenMapper.select(metaMavenId);
    	metaMavenMapper.updateByPrimaryKey(existMetaMavenFile);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    事务执行流程

    步骤事务1事务2
    1beginbegin
    2select * from meta_maven where id = 1
    3select * from meta_maven where id =1
    4update meta_maven set component_status = 1 where id =1
    5update meta_maven set status = 1 where id =1
    6获取黑鸭子扫描结果(超长时间等待)
    7deallock detected
    8update meta_maven set component_status = 2 where id = 1
    9commitrollback

    一般产生死锁的场景(上面的和它不一样,上面的和我们之前介绍的悲观锁发生死锁等待很像):

    步骤事务1事务2
    1beginbegin
    2update table1 set col =1 where id =1
    3update table1 set col1 = 2 where id =2
    4update table1 set col1 = 3 where id = 2
    5update table1 set col1 = 4 where id = 1
    6deaded lockdeaded lock

    报错信息

     Error updating databse, Cause: org.postgressql.util.PSQLException: ERROR: deadlock detected
     Detail: Process 322869 waits for sharelock in transaction 3523519865;
     blocked by process 369631.
    Process 369631 waits foe ShareLock on transaction 3523519865;
    blocked by process 369632.
     where: while updating tuple(22892,30) in relation "meta_maven";ERROR: deadlock detected
    ### SQL: update meta_maven set modifyer = ? where id = ?
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    怎么处理

    我们可以看到,问题很大是出现在hasSecurityExist这个超长等待,

    1. 那么我们把这个方法的事务传播机制改为REQUIRED_NEW或者异步执行(这个逻辑支持异步执行的)
    2. 大事务拆小,设置手动声明事务的方式来提交

    REQUIRED_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起,意思是创建一个新的事务,和原来的事务没有关系

    处理思路;

    1. 大事务拆小,大事务可能发生死锁,如果业务允许,将大事务拆小
    2. 添加合理的索引,没有索引可能会产生表锁,增加死锁发生的概率
    3. 降低隔离级别,如果业务允许,将隔离级别调小是比较好的选择,比如将隔离级别从repeat commit 调整为 read commit.

    场景2优化伪代码:

    
    @Transactional(propagation= Propagation.REQUIRED, isolation= Isolation.READ_COMMITED, timeout = 8, rollbackFor = Exception.class)
    public void download(MetaMavenFile metaMavenFile, HttpResponse response){
    	mataMaven existMetaMaven = metaMavenMapper.select(metaMavenFile);
    	// 处理死锁问题,这个方法单独抽出来,单独声明事务传播机制
    
    	hasSecurityExist(existMetaMaven.getId());
    }
    
    @Transaction(propagation= Propagation= Propagation.REQUIRES_NEW)
    public void updateCompentScaning(Long metaMavenId) {
    	mataMavenMapper.updateCompoentStatus(metaMavenId, ComponentStatus.SCANNING, null);
    }
    
    @Transaction(propagation= Propagation= Propagation.REQUIRES_NEW)
    public void hasSecurityExist(Long metaMavenId) {
     	JSONObject scanResult = getblackDuckScanResult(metaMavenId);
     	mataMaven existMetaMaven = metaMavenMapper.select(metaMavenId);
    	metaMavenMapper.updateByPrimaryKey(existMetaMavenFile);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    【C++】AVL树插入过程详解
    一个去掉PDF背景水印的思路
    某攻防演练心得之随笔记
    [ubuntu]修改时区和更新时间
    【MySQL】库的操作
    VB.net进行CAD二次开发(四)
    Unreal回放系统剖析(下)
    LLM(大语言模型)解码时是怎么生成文本的?
    【C++】map的使用
    不会俄语可以去俄罗斯吗
  • 原文地址:https://blog.csdn.net/u013565163/article/details/126657464