一次mysql死锁的排查过程

来源:互联网 发布:windows文件共享端口 编辑:程序博客网 时间:2024/05/17 08:54
一、背景 
  在测试环境测试给用户并发发送卡券时,出现了死锁,但看代码没有死锁,问题如下图 



  看日志确实发生了死锁,按照死锁产生的原因:一般死锁是两把锁两个人争抢,每个人都获得其中一把,谁都不让谁,等待对方释放锁,死循环导致的,图示如下 
 
  
  

二、问题点 
1. ### SQL: select * from score_user where user_id = ? for update,这个sql查询是发送了死锁 

三、排查过程 
1. 根据经验和死锁产生的条件,猜测代码并发执行,一个线程先锁住了表A的记录,另外一个线程由于原因没有线索表A记录,而锁住了表B的记录,接下来,锁住A记录的线程等待B的锁是否,锁住B的线程等待A的锁释放,所以产生了原因,所以先看代码下载

2. 代码如下面所示,可以看到,基本逻辑,都是先插入score_gain_stream 
Java代码  收藏代码
  1. @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED,rollbackFor = Exception.class)  
  2.     public boolean generateScoreInfo(String userId, Integer score,  
  3.             Long scoreRuleId, int scoreType, int scoreStatus, String scoreWay,  
  4.             String orderId, String inviteeId, String reqId, Integer eventVersion) {  
  5.   
  6.         //0:参数判断  
  7.         if(null == score || score <= 0) {  
  8.             log.warn("score null or < 0, userId:" + userId + "scoreRuleId:" + scoreRuleId + ",scoreWay:" + scoreWay);  
  9.             return true;  
  10.         }  
  11.           
  12.         //1:获取用户等级     
  13.         int memberLevel = MemberLevel.GENERAL_MEMBER;  
  14.           
  15.         ScoreUser dbScoreUser = scoreUserManager.getScoreUserByUserIdForUpdate(userId);  
  16.         boolean isCreate = null == dbScoreUser ? true : false;  
  17.         if (!isCreate) {  
  18.             memberLevel = dbScoreUser.getMemberLevel();  
  19.         }  
  20.           
  21.         // 2:构造/生成积分流水  
  22.         ScoreGainStream scoreGainStream = contructSocreGainStream(userId, score, scoreRuleId, scoreType, scoreStatus, scoreWay,  
  23.                 orderId, inviteeId, reqId, eventVersion,memberLevel);  
  24.           
  25.         boolean streamFlag = addScoreGainStream(scoreGainStream);  
  26.           
  27.         if(!streamFlag){  
  28.             log.error("addScoreGainStream error,data:" + scoreGainStream.toString());  
  29.             return false;  
  30.         }  
  31.           
  32.         // 3:判断用户类型  
  33.         if(isCreate){//新增积分用户信息  
  34.             try {  下载
  35.                 boolean addFlag = addScoreUser(userId, memberLevel, scoreType, score);  
  36.                 if(!addFlag){  
  37.                     log.error("generateScoreInfo addScoreUser error, userId:" + userId + "|" + "score:" + score );  
  38.                     throw new RuntimeException("generateScoreInfo addScoreUser error");  
  39.                 }  
  40.             } catch (Exception e) {  
  41.                 if(e instanceof DuplicateKeyException){  
  42.                     log.warn("addScoreUser DuplicateKeyException,userId:" + userId + "|" + "score:" + score);  
  43.                     //查询用户信息  
  44.                     ScoreUser updateUser = contructUpdateScoreUser(scoreUserManager.getScoreUserByUserIdForUpdate(userId), score, scoreStatus);  
  45.                       
  46.                     boolean flag = scoreUserManager.updateUserScoreInfoById(updateUser) > 0 ? true : false;  
  47.                     if(!flag){  
  48.                         log.error("generateScoreInfo updateUserScoreInfoById error, data:" + updateUser.toString());  
  49.                         throw new RuntimeException("generateScoreInfo updateUserScoreInfoById error");  
  50.                     }  
  51.                       
  52.                     return true;  
  53.                       
  54.                 }else{  
  55.                     log.error("addScoreUser error,userId:" + userId + "|" + "score:" + score, e);  
  56.                     return false;  
  57.                 }  
  58.             }  
  59.               
  60.             return true;  
  61.               
  62.         }else{//更新积分用户信息  
  63.             ScoreUser updateScoreUser = contructUpdateScoreUser(dbScoreUser, score, scoreStatus);  
  64.               
  65.             boolean flag = scoreUserManager.updateUserScoreInfoById(updateScoreUser) > 0 ? true : false;  
  66.             if(!flag){  
  67.                 log.error("generateScoreInfo updateUserScoreInfoById error, data:" + updateScoreUser.toString());  
  68.                 throw new RuntimeException("generateScoreInfo updateUserScoreInfoById error");  
  69.             }  
  70.               
  71.             return true;  
  72.         }  
  73.     }  

3. 看代码,不会发生死锁的,多个线程同时在执行,每个线程都开启事务,每个线程都加锁查询score_user,发现都没有查询到,那么每个线程都执行插入score_gain_stream操作,都成功,接下来,进行插入score_user,这里面只有一个线程可以成功下载,有唯一主键,其他线程这里会报错,接下来代码抓取异常,进行加锁查询,此时报错,死锁了
0 0
原创粉丝点击