20200502补充:
MySQL事务的实现原理
https://www.jianshu.com/p/4957e0346602
-------------------------------------分割线-------------------------------------
原文链接:
https://www.jianshu.com/p/68d5130b25fb
https://www.cnblogs.com/balfish/p/8298296.html
https://blog.csdn.net/weixin_37934748/article/details/82774230
https://www.cnblogs.com/myseries/p/10834172.html
1、什么是事务?为什么要有事务?
事务就是把多件事情当做一件事情来处理。将一些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(最小的工作逻辑单元)。
事务的提出是为了解决并发情况下数据一致性的问题。
最常见的就是转账的场景:
小明给小红转账100元,假设转账分为两步完成
(1)小明减少1000 update test set money = money -100 where name= ‘小明’
(2)小红增加1000 update test set money = money +100 where name= ‘小红’
如果在完成了第1步的时候突然宕机了,小明的钱减少了而小红的钱没有增加那小明岂不是白白丢了100元;这时候就需要用到我们的事务了,并且需要具有如下特性。
2、事务的四大特性
(1)原子性
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,是一组不可再分割的操作集合(最小的工作逻辑单元)。
(2)一致性
一致性是指事务必须使数据库从一个一致性状态转换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
(3)隔离性
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
(4)持久性
持久性是指一个事物一旦被提交了,那么对数据库中数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
3、事务的隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读这几类问题。其中Read committed(提交读) 、Repeatable read (重复读)最常用。
(1)未提交读
这是最低的隔离级别。试想两个事务,一个事务中update的数据还未提交,就影响到了另一个事务,另一个事务在查询的时候就能看到update的结果。这种情况也叫脏读,即一个事务A读取了被另一个事务B修改,但是还未提交的数据。脏读在英文里是dirty read的意思,认为读到的是脏数据,为什么叫脏数据呢,因为是别人动过的数据。就像有洁癖的人,看到别人动过的东西都认为是脏东西一样,而不会嫌自己脏。
(2)提交读
也叫不可重复读(不建议这么记),比未提交读略严格一点,事务之间不那么容易互相影响了。只有A事务中提交的修改,B事务中才能查询到。是Oracle的默认级别
(3)重复读
MySQL的默认级别,在整个事务过程中,对同一笔数据的读取结果是相同的,不管其他事务是否在对共享数据进行更新,也不管更新提交与否。但是这只针对更新的情况,对于插入操作不管用,依然会受到其他事务插入操作的影响。也就是会发生幻读。顾名思义,就像是幻影一样的幻行,事务A中插入或者删除数据,事务B中可以看到数据行数有增加或减少,就像看花眼一样的幻象似的。
20201020更新补充:https://www.cnblogs.com/liyus/p/10556563.html
20210830
https://blog.csdn.net/wxb880114/article/details/105840286
(4)序列化(serializable)
翻译成线性化更好理解一些,这是最严格的隔离级别。简单粗暴,不同事务要线性化执行,即排好队一个一个来,当一个事务未结束时,其他事务不能插手操作。这样固然可以解决脏读、不可重复读和幻读的问题,但是增强隔离性的同时无疑降低了性能。通常会用其他并发级别加上相应的并发锁机制来取代它。
4、spring中的事务
Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。
Spring事务管理涉及的接口的联系如下:
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
5、事务的传播特性
7种传播行为:
PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
虽然有7种,但是常用的就第一种REQUIRED和第四种REQUIRES_NEW
五个隔离级别:
ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
另外四个与JDBC的隔离级别相对应;
ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。
ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。
事务的属性可同通过注解方式或配置文件配置。通常采用声明式事务实现。
底层原理:Spring事务其实就是Spring AOP,底层创建动态代理对象,在代码的开头结尾封装了开启事务和事务回滚操作。
知识点:
事务超时:@Transactional(timeout = 60)
如果用这个注解描述一个方法的话,线程已经跑到方法里面,如果已经过去60秒了还没跑完这个方法并且线程在这个方法中的后面还有涉及到对数据库的增删改查操作时会报事务超时错误(会回滚)。
如果已经过去60秒了还没跑完但是后面已经没有涉及到对数据库的增删改查操作,那么这时不会报事务超时错误(不会回滚)。
回滚:
Spring管理事务默认回滚的异常是什么?
答案是 RuntimeException或者Error。
注意:如果事务在try{}catch(Exception e){e.printStackTrace();}中跑,并且catch中只是打印e的话,那么事务不会rollback。因为异常被catch掉了,框架不知道发生了异常。
如果想要rollback,可以加上rollbackFor=Exception.class,然后:
①在方法上添加 throws Exception,将方法中出现的异常抛出给spring事务,
②去掉方法体中的try catch
③catch (Exception e) { throw e;}继续向上抛,目的是让spring事务捕获这个异常。
rollbackFor=Exception.class,catch(){
throw new RunTimeException();
}
如果不加rollbackFor=Exception.class,抛出new Exception() 是不会回滚的。Spring源码如下:
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
如果是RuntimeException或Error的话,就返回True,表示要回滚,否则返回False,表示不回滚。
只有spring事务捕获到Exception异常后,@Transactional(rollbackFor=Exception.class),才会起到应有的作用;catch (Exception e) { e.printStackTrace(); }这句是捕获try中出现的Exception然后将异常信息打印出来,仅仅是打印出来,然后什么也没干。
@Transactional(timeout = 60,rollbackFor=Exception.class)与rollbackFor=Exception.class的作用是
1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
checked Unchecked exception是运行时错误。