事务基本信息
事务的特性
原子性: 事务一旦开始,后面所有的操作要么全部成功要么全部不成功,不允许停留在中间态,中间态对外不可见
一致性: 事务开始和结束后,数据的完整性没有被破坏;比如: A转账给B,不能出现A的钱被扣了,但B没收到钱。
博客上看到了这么一句话感觉很合理: 一致性是事务的最终目的,原子性、隔离性、持久性都是为了实现一致性。
持久性: 事务的所有操作都必须持久化,哪怕出现宕机、停电等极端情况,重启后也不能丢失数据。
隔离性: 多个事务之间互不干扰,A事务先执行就不能读取到B事务的任何操作。
事务的隔离级别
事务要保证完全的隔离性,就必须对所有的操作按照表锁进行互斥,这样数据的安全性(正确性)是满足了,但是并发性能过低,于是出现了事务隔离级别。这样一来虽然提高了性能,但损失了一致性,这样一来就需要根据各自业务要求合理的选择适合的隔离级别;
Mysql默认可重复读级别解决了不可重复读问题,部分解决了幻读的问题(当前读获取数据会产生幻读,快照读不会)。
下面针对各个事务隔离级别进行说明
- 读未提交
可以读取另一个事务还没提交的数据
- 读已提交
只能读取事务已提交的数据
- 可重复读(Mysql默认)
同一个事务内,多次读取的数据是一致的
- 串行化
所有的读写操作按顺序执行,效率很差
从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。
事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。
不同隔离级别产生的问题
脏读: 两个事务A和B,A读取到了B修改但未提交的数据,此时如果B回滚了,那么A拿到的数据就是临时且无效的脏数据
幻读: 两个事务A和B,A读取了一个字段,然后B插入了一些新行,A再去读取的时候发现结果多了几行
不可重复读: 两个事务A和B,A读取了一个字段,然后B修改了这个字段,之后A再去读这个字段,发现字段值不一致
幻读和不可重复读区别
从总得结果上看,两者很相似,都表现为两次读取的数据不一致;从如何避免问题的角度上看还是有很大区别的;
- 不可重复读只需要加上行锁就可以避免,幻读需要加上表锁才可以避免;
- 不可重复读重点在于update,幻读的重点在于insert和delete,行锁可以控制不能操作update和delete,但是挡不住insert操作;
- 两者最大的区别在于如何通过锁机制解决问题;
隔离级别不同对这三个问题的解决程度
隔离级别 | 脏读 | 幻读 | 不可重复读 |
读未提交 | 未解决 | 未解决 | 未解决 |
读已提交 | 解决 | 未解决 | 未解决 |
可重复读 | 解决 | 未解决 | 解决 |
串行化 | 解决 | 解决 | 解决 |
只有串行化解决了所有的问题,其他三个隔离级别都有缺陷
Spring 包装的事务
Spring 事务实现的方式有哪些?
编程式事务: 通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
声明式事务: 建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
Spring事务的实现方式和实现原理
Spring事务的本质其实就是对数据库事务的支持,没有数据库的事务支持,Spring无法提供事务功能。真正数据库层面的事务提交和回滚是通过binlog或者redo log实现的
Spring事务的传播行为
- REQUIRED(默认)
业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务。这是spring默认的传播行为。
- NOT_SUPPORTED
声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。
- REQUIRESNEW
不管是否存在事务,该方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。
- MANDATORY
该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出异常。
- SUPPORTS
该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。(有事务就加入,没有也无所谓)
- NEVER
该方法绝对不能在事务范围内执行。如果在就抛异常。只有该方法没有关联到任何事务,才正常执行。
- NESTED
如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。
假设A使用默认隔离级别,B使用NESTED那么此时如果B抛异常,A正常则B先回滚,A再正常提交;如果A抛异常则A和B一起回滚
Spring事务管理有哪些优点
- 为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。
- 为编程式事务管理提供了一套简单的API而不是一些复杂的事务API
- 支持声明式事务管理。
- 和Spring各种数据访问抽象层很好得集成。
分布式事务
分布式事务顾名思义就是要在分布式系统中实现事务,它其实是由多个本地事务组合而成。
对于分布式事务而言几乎满足不了 ACID,其实对于单机事务而言大部分情况下也没有满足 ACID,不然怎么会有四种隔离级别呢?所以更别说分布在不同数据库或者不同应用上的分布式事务了。
- 2PC(两阶段提交)
引入事务协调者,参与者概念,把事务分为准备阶段和提交阶段,但是一种同步阻塞的模型;协调者有超时机制但参与者没有
- 3PC(三阶段提交)
2PC的变种,事务分为准备阶段,预提交阶段,提交阶段。参与者也引入了超时机制
- TCC(Try - Confirm - Cancel)
业务层面的事务,不再局限于数据库操作。Try是预留资源,Confirm是确认执行,Cancel是撤销,对业务侵入较大和业务强耦合还需要保证操作的幂等性
- 本地消息表
- 消息事务
- 最大努力通知
- 详情参考 面试必问:分布式事务六种解决方案