go语言 mysql卡死_一次mysql死锁的排查过程-Go语言中文社区

一次mysql死锁的排查过程一、背景17号晚上要吃饭了,看旁边的妹子和佐哥还在调代码,就问了下什么问题啊,还在弄,妹子说,在测试环境测试给用户并发发送卡券时,出现了死锁,但看代码没有死锁,问题如下图

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

5a18ff0a77538bce74e7a5103588e0b1.png不过这次说看代码没有问题,感觉这个问题比较诡异,跟他们说先吃饭,吃完,一起群力群策研究研究这个。二、问题点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代码  62d2a4b1c06971600377178cae2023da.png

@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 

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,这里面只有一个线程可以成功,有唯一主键,其他线程这里会报错,接下来代码抓取异常,进行加锁查询,此时报错,死锁了4. 理论上,报错,这里没有涉及争抢资源的情况,大家都在等待score_user释放,就一个锁,怎么会死锁呢,看来代码解决不了问题了5. 再去查下mysql的死锁日志,看看死锁具体怎么产生的,如下图链接如何查询死锁日志http://825635381.iteye.com/blog/2339503

00c80653a8b6cb24a1566336851c12eb.png看紫色中的三部分,TRANSACTION 1292943095需要RECORD LOCKS space id 553 page no 376 n bits 368 index `index_user_id` of table `tbj`.`score_user这个位置的X锁,一直等待这个X锁TRANSACTION 1292943097这个已经持有RECORD LOCKS space id 553 page no 376 n bits 368 index `index_user_id` of table `tbj`.`score_user这个位置的S锁,这样导致TRANSACTION 1292943095无法在这个位置获得X锁TRANSACTION 1292943097这个事务接下来也在RECORD LOCKS space id 553 page no 376 n bits 368 index `index_user_id` of table `tbj`.`score_user这个位置的等待X锁所以问题点有了: 

1. 为什么有一个线程会持有S锁,看前面的代码结构没有加过S锁?

2. 还有为什么TRANSACTION 1292943097这个事务不能继续加X锁提交?6.这边开始排查为什么会有S锁,查了很多资料,终于,在官网文档查询到了,如下[b]

Java代码  62d2a4b1c06971600377178cae2023da.png

INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.Prior to inserting the row, a type of gap lock called an insertion intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock.

大体的意思是:insert会对插入成功的行加上排它锁,这个排它锁是个记录锁,而非next-key锁(当然更不是gap锁了),不会阻止其他并发的事务往这条记录之前插入记录。在插入之前,会先在插入记录所在的间隙加上一个插入意向gap锁(简称I锁吧),并发的事务可以对同一个gap加I锁。如果insert 的事务出现了duplicate-key error ,事务会对duplicate index record加共享锁。这个共享锁在并发的情况下是会产生死锁的,比如有两个并发的insert都对要对同一条记录加共享锁,而此时这条记录又被其他事务加上了排它锁,排它锁的事务提交或者回滚后,两个并发的insert操作是会发生死锁的。

[/b]原理分析:这就找到上面问题为什么加上S锁的问题,当并发插入时,出现duplicate异常时,mysql会默认加上S锁,这就是为什么会出现死锁日志里面有个事务加上S锁了,也就同时解释了第二个问题,为什么事务没能提交,因为第一个事务也发生了duplicate异常,同时也对同一个位置加上了S锁,这样就出现了一种情况,多个线程对同一个位置持有S锁,每个线程都去这个位置争抢X锁,S和X锁两者是互斥关系,所以出现循环等待,死锁就此产生关于mysql锁的机制,单独写个博客来介绍四、解决办法1. 并发插入时,不在一个事务内进行再次事务提交2. 通过其他手段,如预创建账户,解决这个要并发插入的问题3. 改并发为串行执行五、解决过程六、问题总结1. mysql并发插入,出现duplicate时,会默认加S锁,这个坑啊,坑啊,要研究下为什么这么加七、为什么会发生?1. 知识体系,需要再次完善,技术无止境

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/551755.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么格式化电脑_U盘格式化后数据能恢复吗?人人都能学会的恢复方法!

获取专业数据恢复软件&#xff1a;专注硬盘U盘误删文件数据恢复软件免费下载​dl-next.aunbox.cn数据恢复官网&#xff1a;嗨格式数据恢复大师官网 - 专业U盘/电脑/硬盘数据恢复软件_免费下载​huifu.hgs.cnU盘格式化后数据能恢复吗&#xff1f;U盘在我们生活中算是比较常用的数…

java 运算符_详解Java表达式与运算符

课程导言【变量的赋值与计算都离不开表达式&#xff0c;表达式的运算依赖于变量、常量和运算符。本节课讨论Java的表达式的构成、常量的定义、运算符的分类及应用。通过本课的学习你将掌握运用表达式和运算符完成变量赋值、条件判断、数学运算、逻辑运算等功能操作】在讲述课程…

将你一张表的值覆盖_精准度可达亚米级,山东“北斗一张网”向社会免费开放...

齐鲁晚报齐鲁壹点记者张阿凤通讯员苏彬8月21日&#xff0c;山东省北斗卫星导航定位基准站网(以下简称“北斗一张网”)推广应用座谈会在济南举行。“北斗一张网”自2019年8月建成后&#xff0c;现已免费向社会提供实时、动态、高精度的卫星导航定位基础服务&#xff0c;在自然资…

okhttp 工具类_日语学习工具推荐,小白必备!

有很多同学问我&#xff0c;有没有好用的日语学习工具&#xff1f;当然有啦~这些学习工具大概分为辞典类、翻译类、日语知识类等等。今天呢&#xff0c;在自身使用的工具里挑选了下面8个学习工具网站&#xff0c;适合各个学习阶段&#xff0c;从日语初级到精通&#xff0c;它们…

des和aes相比较有哪些特点_栓流气力输送相比较传统的高速气力输送方式而言,有哪些优势?...

南京翔瑞粉体&#xff1a;“气力输送”是指运用物料载体压缩空气&#xff08;或气体&#xff09;&#xff0c;将散装物料从一处输送到另一处的输送系统&#xff0c;与传统的输送机械相比&#xff0c;气力输送系统在输送过程中不易出现沾染粉尘、受潮、污损等现象&#xff0c;并…

32位java虚拟机_微软Java虚拟机-Microsoft VM32位下载V5.0.3805.0安装版-MicrosoftJavaVirtualMachine西西软件下载...

Microsoft VM32位是一款微软Java虚拟机(Microsoft Java Virtual Machine)&#xff0c;为IE浏览器提供Java支持。Microsoft VM for Java 是在由Microsoft开发类别 Servers Shareware 软件。安装在WinNT上之前必须打有 NT4 Service Pack 3 以上。安装检测检查是否已安装 Microsof…

iframe src 传参数_剧本杀测评|本友投稿——蜀山传(非剧透)

点击蓝字关注“我爱剧本杀”以下内容不会剧透请放心阅读本友投稿——《蜀山传》前言&#xff1a;这一次的“本友测评”真的是早早早早早鸟测评了&#xff0c;更加难得的是&#xff0c;这一次是曾经制作发行过《夜来香》的发行团队小本买卖和作者紧张齐聚南宁并亲自带本&#xf…

gnss单频软件接收机应用与编程_GNSS/GPS RTK定位 (手机,无人车定位,无人驾驶,因子图优化)...

Global navigation satellite system (GNSS)是手机或者无人车定位中的关键一个部分。GNSS是当前主要的可以提供绝对定位信息的一种信息来源。无人车的基于地图匹配定位的这一个部分中&#xff0c;GNSS经常用来提供初始化。就目前来看&#xff0c;GNSS的定位方式主要包括单点定位…

windows系统改装为linux系统_Linux怎么克隆系统?备份系统跟Windows系统有区别吗?...

请关注本头条号&#xff0c;每天坚持更新原创干货技术文章。如需学习视频&#xff0c;请在微信搜索公众号“智传网优”直接开始自助视频学习1. 前言本文主要讲解在Linux系统中怎么克隆系统镜像、克隆分区、整盘克隆。它跟Windows系统有何区别&#xff1f;您可能想要克隆Linux分…

c++ hough变换代码_hough变换原理以及实现(转载)

原理链接如下&#xff1a;陌归&#xff1a;霍夫&#xff08;Hough&#xff09;变换之直线检测代码链接&#xff1a;Ganso&#xff1a;Fundamentals——从车道线检测谈到霍夫变换同样是一篇讲解原理的番外&#xff0c;这一篇主要讲解CV中常用的霍夫变换的数学原理。霍夫变换的由…

java filter url匹配规则_java过滤器filter使用

一&#xff1a;filter:过滤器&#xff0c;拦截servlet的请求和响应。1、1 package jd.com.filter;23 import javax.servlet.*;4 import java.io.IOException;56 public classMyFilter implements Filter {7 Override8 public voiddestroy() {910 }1112 Override13 public voidi…

怎么对document.write写出来的内容调整对齐方式_干不过写PPT的?麦肯锡老阿姨教你4招...

PPT是我真正花过10,000小时以上的技能&#xff0c;毕竟在麦肯锡呆了7年。麦肯锡PPT重视内容与逻辑&#xff0c;我改行做营销后又开始注意视觉效果。怎么干过我们这些画PPT的&#xff1f;&#xff1a;)从内容到形式&#xff0c;我来讲讲4步流程&#xff0c;有助于提高效率。/ 01…

iphone查看删除的短信_苹果删除的短信

苹果删除的短信怎么恢复?大家在使用手机的时候&#xff0c;有些没用的短信就习惯清除了&#xff0c;但是有时候比较重要的短信很可能也会误删&#xff0c;后期想要找确又找不到&#xff0c;那么苹果删除的短信能恢复吗?怎么恢复呢?下面就来详细介绍一下。苹果删除的短信怎么…

vscode代码运行时间工具_代码编辑器横评:为什么 VS Code 能拔得头筹

2015 年 4 月 29 日的 Build 大会上&#xff0c;微软发布了 Visual Studio Code 第一个预览版本。短短四年时间里&#xff0c;VS Code 高速成长。根据 2019 年 2 月的 PYPL Top IDE index 的排名&#xff0c;VS Code 的涨势迅猛&#xff0c;在所有编辑器与 IDE 中排名第六&…

ie浏览器网页版进入_Win10系统中IE和edge浏览器无法打开网页如何解决

Win10系统中IE和edge浏览器无法打开网页如何解决最近有Win10系统用户们反馈最新Win10系统中的IE和edge浏览器都无法上网&#xff0c;打不开网页&#xff0c;而第三方浏览器却可以&#xff0c;遇到这样的情况该如何解决呢?引起IE和edge浏览器无法打开网页的可能原因&#xff0c…

cad插件_CAD素材与插件合集

CAD素材与插件合集CAD字体包下载链接&#xff1a;https://pan.baidu.com/s/16Wq4boqm254qJNJG5fD5EA提取码&#xff1a;h28nCAD经典模式下载链接&#xff1a;https://pan.baidu.com/s/18U6vSetQxg6bCNJDElZtFA提取码&#xff1a;wlje电气符号下载链接&#xff1a;https://pan.b…

一个form 如何做两次提交_如何做一个自信魅力的女人

有一种常见的说法是外观完全决定了吸引力&#xff0c;错&#xff0c;如果你希望人们被你吸引&#xff0c;如果你想做一个优秀的、成功的、万众瞩目的女性&#xff0c;那么你必须做很多努力&#xff0c;才能成为一个有魅力的人。1做自己不要复制像杰西卡兰格或安德里亚汤普森这样…

vbs代码炫酷效果_Python|实现黑客帝国代码雨效果

Python|实现黑客帝国代码雨效果估计大家都看过电影《黑客帝国》吧&#xff0c;片中的一段代码雨片段实在是炫酷&#xff0c;试想一下&#xff0c;片中的代码雨效果在自己电脑屏幕上实现了会是一种什么样的感觉&#xff0c;会不会有种身临黑客之境呢&#xff1f;本着满足好奇心和…

电脑手写输入法_百度输入法“手写输入”为什么不是老年人的专利?AI的进步...

智能手机的不断普及&#xff0c;也让人机交互取得了很快的发展&#xff0c;就从大家一下常用的设计输入法来看&#xff0c;效果也是越来越智能化了&#xff0c;一个输入法的自我修养选择gboard的原因有很多种&#xff0c;这也是作为一款我国国民输入法的第一要素&#xff0c;毕…

台式电脑如何设置开机密码_设置苹果Mac电脑的开机密码-macw资讯

有很多用户刚从windows系统转过来使用mac系统&#xff0c;可能有很多东西都不知道在哪里&#xff0c;不知道怎么去设置&#xff1f;下面我们就来看下mac是怎样设置开机密码的。非常简单&#xff0c;来跟小编一起来学习然后设置苹果Mac电脑的开机密码吧&#xff01;1、首先&…