一次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
3. 看代码,不会发生死锁的,多个线程同时在执行,每个线程都开启事务,每个线程都加锁查询score_user,发现都没有查询到,那么每个线程都执行插入score_gain_stream操作,都成功,接下来,进行插入score_user,这里面只有一个线程可以成功下载,有唯一主键,其他线程这里会报错,接下来代码抓取异常,进行加锁查询,此时报错,死锁了
在测试环境测试给用户并发发送卡券时,出现了死锁,但看代码没有死锁,问题如下图
看日志确实发生了死锁,按照死锁产生的原因:一般死锁是两把锁两个人争抢,每个人都获得其中一把,谁都不让谁,等待对方释放锁,死循环导致的,图示如下
二、问题点
1. ### SQL: select * from score_user where user_id = ? for update,这个sql查询是发送了死锁
三、排查过程
1. 根据经验和死锁产生的条件,猜测代码并发执行,一个线程先锁住了表A的记录,另外一个线程由于原因没有线索表A记录,而锁住了表B的记录,接下来,锁住A记录的线程等待B的锁是否,锁住B的线程等待A的锁释放,所以产生了原因,所以先看代码下载
2. 代码如下面所示,可以看到,基本逻辑,都是先插入score_gain_stream
- @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED,rollbackFor = Exception.class)
- public boolean generateScoreInfo(String userId, Integer score,
- Long scoreRuleId, int scoreType, int scoreStatus, String scoreWay,
- String orderId, String inviteeId, String reqId, Integer eventVersion) {
- //0:参数判断
- if(null == score || score <= 0) {
- log.warn("score null or < 0, userId:" + userId + "scoreRuleId:" + scoreRuleId + ",scoreWay:" + scoreWay);
- return true;
- }
- //1:获取用户等级
- int memberLevel = MemberLevel.GENERAL_MEMBER;
- ScoreUser dbScoreUser = scoreUserManager.getScoreUserByUserIdForUpdate(userId);
- boolean isCreate = null == dbScoreUser ? true : false;
- if (!isCreate) {
- memberLevel = dbScoreUser.getMemberLevel();
- }
- // 2:构造/生成积分流水
- ScoreGainStream scoreGainStream = contructSocreGainStream(userId, score, scoreRuleId, scoreType, scoreStatus, scoreWay,
- orderId, inviteeId, reqId, eventVersion,memberLevel);
- boolean streamFlag = addScoreGainStream(scoreGainStream);
- if(!streamFlag){
- log.error("addScoreGainStream error,data:" + scoreGainStream.toString());
- return false;
- }
- // 3:判断用户类型
- if(isCreate){//新增积分用户信息
- try { 下载
- boolean addFlag = addScoreUser(userId, memberLevel, scoreType, score);
- if(!addFlag){
- log.error("generateScoreInfo addScoreUser error, userId:" + userId + "|" + "score:" + score );
- throw new RuntimeException("generateScoreInfo addScoreUser error");
- }
- } catch (Exception e) {
- if(e instanceof DuplicateKeyException){
- log.warn("addScoreUser DuplicateKeyException,userId:" + userId + "|" + "score:" + score);
- //查询用户信息
- ScoreUser updateUser = contructUpdateScoreUser(scoreUserManager.getScoreUserByUserIdForUpdate(userId), score, scoreStatus);
- boolean flag = scoreUserManager.updateUserScoreInfoById(updateUser) > 0 ? true : false;
- if(!flag){
- log.error("generateScoreInfo updateUserScoreInfoById error, data:" + updateUser.toString());
- throw new RuntimeException("generateScoreInfo updateUserScoreInfoById error");
- }
- return true;
- }else{
- log.error("addScoreUser error,userId:" + userId + "|" + "score:" + score, e);
- return false;
- }
- }
- return true;
- }else{//更新积分用户信息
- ScoreUser updateScoreUser = contructUpdateScoreUser(dbScoreUser, score, scoreStatus);
- boolean flag = scoreUserManager.updateUserScoreInfoById(updateScoreUser) > 0 ? true : false;
- if(!flag){
- log.error("generateScoreInfo updateUserScoreInfoById error, data:" + updateScoreUser.toString());
- throw new RuntimeException("generateScoreInfo updateUserScoreInfoById error");
- }
- return true;
- }
- }
3. 看代码,不会发生死锁的,多个线程同时在执行,每个线程都开启事务,每个线程都加锁查询score_user,发现都没有查询到,那么每个线程都执行插入score_gain_stream操作,都成功,接下来,进行插入score_user,这里面只有一个线程可以成功下载,有唯一主键,其他线程这里会报错,接下来代码抓取异常,进行加锁查询,此时报错,死锁了
0 0
- 一次mysql死锁的排查过程
- 一次mysql死锁的排查过程
- 一次Mysql死锁排查过程的全纪录
- 记录一次 Mysql 死锁排查过程
- 我的Mysql死锁排查过程(案例分析)
- 记一次死锁问题的排查
- 第一次遇到死锁——记一次程序卡住问题的错误排查过程
- 记一次死锁问题的排查和解决
- 一次segfault错误的排查过程
- 一次segfault错误的排查过程
- 一次线上OOM过程的排查
- 一次频繁Full GC的排查过程
- 记一次线上问题的排查过程
- 记一次 BUG 的排查过程
- mysql 一次死锁的处理
- mysql 一次死锁的处理
- 排查mysql死锁
- mysql 排查死锁
- Ruby Rails入门——windows下搭建Ruby Rails Web开发环境
- web标准的理解
- github代码托管+git bash/android studio
- Qt5:QML:LocalStorage
- SpringBoot获取properties配置
- 一次mysql死锁的排查过程
- 用Altium Designer 在PCB板中制作LOGO的方法详解
- VS2012中自动变量的地址分配问题
- SYS_LAST_KMSG里的hw_status和fiq step的含义
- 构造素数表
- 2016 cocoapods的安装和使用以及版本升级遇到的问题 [转]
- winsock2.h的链接库文件问题
- Android Git代码版本控制
- QTcpSocket客户端、服务端互发字符串简单示例