一、什么是事务:
事务是逻辑上的一组操作,要么执行,要么不执行
二、事务的特性(ACID)
原子性:事务是最小的执行单元,不允许分割。确保事务的执行要么全部完成,要么失败。
一致性:执行事务前后,数据保持一致。数据库总是从一个一致性的状态转换到另一个一致性的状态。
隔离性:并发访问数据库时,一个用户的事务不会被其他事务所干扰,各并发事务之间的数据库是独立的。(通俗的讲,隔离性就是指控制另外一个事务可以看到本事务内的哪些数据)------ 补充说明: 通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。这里所说的“通常来说”是指,不同的数据库存储引擎实现会有些差异,在平衡性能和数据一致性问题上。
持久性:一个事务一旦提交。它对数据库的影响是持久的,及时数据库发生故障也不应该对其有任何影响。
隔离级别
隔离级别其实比想象的要复杂,在SQL标准中定义了四种隔离级别,每一种级别都规定了,一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。(较低级别的隔离通常可以执行更高的并发,系统的开销也更低,其实隔离级别的选择,就是在平衡数据一致性和并发性能)
四种隔离级别:
READ UNCOMMITTED (未提交读)
在READ UNCOMMITTED 级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。 这个级别会导致很多问题(在实际应用中一般很少使用)
READ COMMITED (提交读)
大多数数据库系统的默认隔离级别都是这个(但是MySQL 不是),READ COMMITED 满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改,换句话说,一个事务开始直到提交之前,所做的任何修改对其他事务都是不可见的。所以,这个级别有时候也会被称为不可重复读。(注:这里的不可重复读指的是对同一记录数据的读取,多次读取可能会出现不相同的情况,比如第一次读取在另一个事务提交之前,第二次读取是在另一个事务提交之后,如果另一个事务对该记录进行了修改操作,则会出现两次读取数据不一样的情况)
REPEATABLE READ(可重复读) ——— MqSQL默认的隔离级别
REPEATABLE READ 解决了脏读的问题,该级别保证了在同一个事务中多次读取相同记录的结果是一致的,也就是避免了不可重复读问题。(注意这里所说的多次读取相记录的结果是一致,也就是说,即使另一个事务对该相同记录进行了修改并提交,当前事务也不能读取到另一个事务的修改结果。如果需要在事务中读取到最新的数据,则可以加上for update);
还有一点,可重复读无法解决幻读问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。
需要注意的是,我们在使用MySQL时,一般都是使用InnoDB作为存储引擎,在InnoDB内,通过多版本并发控制(MVCC)解决了幻读的问题。
**SERIALIZABLE(可串行化)**
可串行化作为最高的隔离级别。它通过强制事务串行执行,实际应用中也很少用到这个
所以总结来说:
MySQL + InnoDB 同时解决了隔离性导致的脏读,不可重复读和幻读问题。
注:InnoDB,所有的操作都是事务。
隔离级别。
三、Spring的事务管理接口:
PlatformTransactionManager: (平台)事务管理器
TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
TransactionStatus: 事务运行状态
所谓事务管理,就是按照给定的事务规则来提交或者回滚操作
Spring并不直接管理事务,而是提供了多种事务管理器,他们将具体的事务管理委托给了第三方持久化框架平台,比如JDBC,Mybatis, JTA
那什么是事务属性呢?
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面。
事务属性
1、 事务只读属性(对事物资源是否执行只读操作)
如果事务内只涉及读或者读写操作,可将事务设置为只读事务,这样可以提高性能(为什么能提高性能?)
2、事务的传播行为:(Spring为了解决业务层方法之间相互调用的事务问题)
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
不支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务
作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于
补充
TransactionDefinition.PROPAGATION_REQUIRED。
这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED 是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
3、事务超时属性(一个事务允许执行的最长时间)
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
4、回滚规则(定义事务回滚规则)
这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚(注意如果将runtime异常catch住了,也会导致事务不回滚),而在遇到检查型异常时不会回滚。但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
网友评论