mysql行级锁作用_Mysql事务及行级锁的理解

在最近的开发中,碰到一个需求签到,每个用户每天只能签到一次,那么怎么去判断某个用户当天是否签到呢?因为当属表设计的时候,每个用户签到一次,即向表中插入一条记录,根据记录的数量和时间来判断用户当天是否签到。

这样的话就会有一个问题,如果是在网速过慢的情况下,用户多次点击签到按钮,那么变会发送多次请求,可能会导致一天多次签到,重复提交的问题,那么很自然的想到用事务。这次用的是spring + mybtais的框架,一开始设计的代码大致如下:

public booleansignIn(SignInHistory signInHistory) {//编程式开启事务

TransactionTemplate template = newTransactionTemplate(transactionManager);boolean result = (boolean) template.execute(new TransactionCallback() {publicObject doInTransaction(TransactionStatus transactionStatus) {try{//获取用户所有签到记录

List SignInHistoryList =signInMapper.select(signInHistory);//如果当前时间和List中某条签到时间相同,则当天已签到,代码略去//插入签到历史表

signInMapper.insert(signInHistory);

}catch(Exception e) {

transactionStatus.setRollbackOnly();

logger.error(e);return false;

}return true;

}

});

}

但是在测试中,发现还是会发生重复提交。

那么看mysql文档

Consistent read is the default mode in whichInnoDBprocessesSELECTstatements inREAD COMMITTEDandREPEATABLE READisolation levels. A consistent read does not set any locks on the tables it accesses, and therefore other sessions are free to modify those tables at the same time a consistent read is being performed on the table.

Mysql文档中也有相关说明:如果是在read committed和repeatab read下,普通的select语句并不会进行锁操作。其它session可以照常更新或插入操作。

所以在这里面就可以发现,如果只是普通select,不管在不在事务中,mysql都不会将select加锁,所以根本无法阻止其它事务插入记录。

由此可以得出一个理解,

事务隔离级别

数据库事务隔离级别,只是针对一个事务能不能读取其它事务的中间结果。

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的"幻影"行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

事务传播级别

数据库事务传播级别,指的是事务嵌套时,应该采用什么策略,即在一个事务中调用别的事务,该怎么办

假如有一下两个事务:

ServiceA {

voidmethodA(){

ServiceB.methodB();

}

}ServiceB {

voidmethodB(){

}

}

1:PROPAGATION_REQUIRED

加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务

比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED,那么由于执行ServiceA.methodA的时候,

ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA

的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。

这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被

提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚

2:PROPAGATION_SUPPORTS

如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行

3:PROPAGATION_MANDATORY

必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

4:PROPAGATION_REQUIRES_NEW

这个就比较绕口了。比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,

那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,

他才继续执行。他与PROPAGATION_REQUIRED的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在

两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,

如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

5:PROPAGATION_NOT_SUPPORTED

当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED,

那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

6:PROPAGATION_NEVER

不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED,而ServiceB.methodB的事务级别是PROPAGATION_NEVER,

那么ServiceB.methodB就要抛出异常了。

7:PROPAGATION_NESTED

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,

而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。

而Nested事务的好处是他有一个savepoint。

行级锁

如果有两个事务A,B都有read和write操作,如果逻辑是如果表中没有记录则插入,那么因为read操作并没有加锁,A,B进行read操作时,有可能表中都没有记录,那么事务A,B都会进行插入操作,表中将会有两条记录。

如果要保证在事务并发时,每条事务读取到的数据都是最新的,那么只能采用锁。

在select语句后加上FOR UPDATE,再测试,重复提交的问题被解决了。

但是问题又来了,如果在select语句后加上LOCK IN SHARE MODE,那么会报死锁的错误。

查看mysql文档:

SELECT ... LOCK IN SHARE MODEsets a shared mode lock on any rows that are read. Other sessions can read the rows, but cannot modify them until your transaction commits. If any of these rows were changed by another transaction that has not yet committed, your query waits until that transaction ends and then uses the latest values.

SELECT ... FORUPDATEsets an exclusive lock on the rows read. An exclusive lock prevents other sessions from accessing the rows for reading or writing.

LOCK IN SHARE MODE会在读取的行上加共享锁,其他session只能读不能修改或删除,如果有其他事务修改了记录,那么会等待事务提交后,再读取。

FOR UPDATE在读取行上设置一个排他锁。阻止其他session读取或者写入行数据

这样看起来似乎就能解释为什么使用LOCK IN SHARE MODE会产生死锁了,假如两个事务A、B都读取同一行记录,那么在这一行就加上了共享锁,但是A和B事务中都需要修改这一行,那么都要等待对方释放共享锁才能进行,结果造成了死锁。

只能使用for update来防止死锁和重复插入。

这就是mysql的两种行级锁的区别。

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

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

相关文章

activexobject对象不能创建_Oracle数据库用户管理之系统权限和对象权限

【关键术语】Privileges 权限System privileges 系统权限Object privileges 对象权限Grant 授予Revoke 撤消2.1 Oracle 权限概述2.1.1 权限的作用权限(privilege)是指执行特定类型 SQL 命令或访问其他模式对象的权利。Oracle 使用 权限来控制用户对数据的访问以及用户所能执行的…

视频文件大小计算

1.每小时录像文件大小计算公式: 码率大小*3600/8/1024 MB/小时。2.硬盘录像机硬盘容量计算公式: 每小时录像文件大小*每天录像时间(时)*硬盘录像机路数*需要保存的天数。例如:8路硬盘录像机,音视频录像,采用512Kbps定…

【啃不完的算法导论】- 动态规划 - 最长公共子序列(概念篇)

以下内容纯是为了熟悉《算法导论》中的内容,高手可略过,其中涉及的书本内容的版权归原作者、译者、出版社所有 求最长公共子序列,一个典型的 动态规划题 和 字符串处理算法,写在这里是希望自己以后能多来看看和改改,温…

python发送邮箱_你知道怎么用Python发送邮件吗?

作者 | 陈熹来源 | 早起Python(ID:zaoqi-python)头图 | CSDN 下载自东方IC前言本文主要对邮件操作基础知识及代码进行介绍,用Python发邮件有哪些优势?批量发送邮件,并且各邮件可以不同高度自定义的定时发送更有效地管理收件箱基本…

解决 avformat_alloc_context无法识别的问题

由于最近库更新,如果还是用原来的测试用例会碰到avformat_alloc_context 无法识别的问题 avformat_alloc_context is cannot indentified。 解决办法就是将 ocavformat_alloc_context 改成 ocav_alloc_format_context(); 就ok啦 注:我用的库是FFmpeg-full-SD…

python去空格的函数_Python怎么去掉最后的空格

strip()函数 去空格nrt函数的用法 strip 同时去掉左右两边的空格(推荐学习:Python视频教程) lstrip 去掉左边的空格 rstrip 去掉右边的空格 具体示例如下:>>>a" hello world!! " >&g…

Java中getResource()的用法

用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要 取得c:/test.txt文件,就会这样用File file new Fil…

centos中安装mysql5.6_CentOS中安装MySQL5.6报错的解决方法

由于项目需要,必须使用最新出来的MySQL5.6社区版本,使用的操作系统是CentOS6.3。然后安装到中途报错如下:file /usr/share/mys由于项目需要,必须使用最新出来的MySQL5.6社区版本,使用的操作系统是CentOS6.3。首先&…

python逻辑运算符不懂_Python之逻辑运算符

这一小节我在动笔之前犹豫到底要不要动手写,虽然简单但是防止遗忘,博主还是决定记录一下。Python中运算符主要分为算术运算符,赋值运算符,比较运算符,逻辑运算符以及成员运算符。下面详细记录这5种运算符。 1. 算术运算…

使用Wireshark进行SIP包解析

1. 安装Wireshark 下载Wireshark后,安装很简单,基本上只需要点击“Next”和“I agree”等按钮,不再赘述。 2. Wireshark介绍 参见:http://man.lupaworld.com/content/network/wireshark/Introduction.html copy一下简要介绍&a…

Android-Animations的使用大全之二:Frame Animation和其他

Android-Animations的使用大全之一&#xff1a;Tweened Animations详解 5 Frame-By-Frame Animations的使用方 1 在res/drawable中创建一个xml文件&#xff0c;定义Animation的动画播放序列 anim_nv.xml Xml代码 <animation-list xmlns:android"http://schemas.androi…

wireshark分析SIP协议——注册

SIP 是VOIP目前非常流行的一种协议。有关协议的详细原理参照相关文档。本文通过wireshark抓包分析SIP user agent&#xff08;用户代理客户机&#xff0c;uac&#xff09;与SIPserve之间的交互过程&#xff0c;在拨打SIP电话之前&#xff0c;先需要搭建相应的环境&#xff1a;根…

micropython仿真器_Micropython教程之TPYBoard DIY超声波测距仪实例演示

1.实验目的 1. 学习在PC机系统中扩展简单I/O?接口的方法。 2. 进一步学习编制数据输出程序的设计方法。 3. 学习超声波模块的测距原理。 4. 学习LCD5110接线方法 5. 学习TPYboard控制超声波模块测距。(萝卜学科编程教育tpyboard。com) 2.所需元器件 超声波模块一个 TPYBoard板…

monotouch在ipad中的实例应用--显示图像和文字

本节主要讲述在苹果环境中使用monodevelop开发ipad的一个实例&#xff0c;具体操作如下 1、新建项目 选择monotouch--ipad--Empty project 命名为iPad01 2、添加新文件&#xff0c;选择monotouch--ipad view&#xff0c;命名为showView&#xff0c;这样会在项目中多出来三个文件…

mysql的util_JDBC连接mysql工具类Util供大家参考

> list new ArrayList>();//静态代码块&#xff0c;在程序编译的时候执行static {//创建Properties对象Properties properties new Properties();//获取文件输入流InputStream is JDBCUtil_cj.class.getClassLoader().getResourceAsStream("jdbc.properties"…

打开pjsip2.1版本的视频支持

要打开pjsip2.1的视频支持。 首先需要修改pjmedia\include\pjmedia\config.h文件&#xff0c; 如下所示&#xff0c;修改PJMEDIA_HAS_VIDEO的值为1, 修改PJMEDIA_HAS_FFMPEG的值为1 #ifndef PJMEDIA_HAS_VIDEO # define PJMEDIA_HAS_VIDEO 1 #endif #ifndef…

MySQL数据库在众多表中对表名的查询及预处理存储过程(变量做表名)

以下的文章主要介绍的是MySQL数据库在众多表中进行表名与字段名的查询的实际操作步骤&#xff0c;以及对实现其查询所要用到的SQL 语句的介绍&#xff0c;还有两个实际解决方案的描述&#xff0c;以下就是文章的主要内容描述。 在MySQL 众多表中查找一个表名或者字段名的 SQL 语…

MTU MSS 详解记录

先学习理解一下帧的封装格式&#xff1a; 需要注意的是&#xff0c;区别两种帧封装格式&#xff1a;802标准帧和以太网帧1&#xff0c;在802标准定义的帧格式中&#xff0c;长度字段是指它后续数据的字节长度&#xff0c;但不包括C R C检验码。RFC 1042&#xff08;IEEE 802&a…

MySQL字符集的基本类型与统一字符集

以下的文章主要介绍的是MySQL字符集的基本类型&#xff0c; 统一字符集的实际操作方法&#xff0c;等相关内容的介绍&#xff0c;以下就是MySQL字符集的相关内容的描述&#xff0c;希望你会在以后的学习或是工作中带来很大的帮助。 一. MySQL字符集类型 MySQL服务器中有六个关键…

python socket发包_一个python发包的脚本

#codingutf-8Created on 2016年4月12日author: administraterfrom socket import *import timeHOST 172.16.6.70PORT 7125BUFSIZ 1024ADDR (HOST, PORT)def gatewatTcpClient():sock socket(AF_INET,SOCK_STREAM)sock.connect(ADDR)register_data 7e01004026018691830270…