本篇记录一下关于事务的基础知识
一、事务的基本要素(ACID)
- 原子性(Atomicity)
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。 - 一致性(Consistency)
事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。 - 隔离性(Isolation)
同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。 - 持久性(Durability)
事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
二、四种隔离级别
-
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。 -
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。 -
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。 -
Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
三、事务的并发
- 脏读
事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据 - 不可重复读
事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。 - 幻读
系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
四、spring事务传播机制
事务传播行为类型 | 描述 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中(默认使用这个) |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作,这个是事务的嵌套 |
五、spring事务的重要接口
- spring事务管理器->PlatformTransactionManager
这个接口有三个方法:
1.TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
他的作用是:根据TransactionDefinition 获取事务,返回结果是TransactionStatus。TransactionDefinition 相当于事务的定义,根据事务的定义获取事务,返回的结果是TransactionStatus事务的状态。
2.void commit(TransactionStatus status) throws TransactionException;
通过TransactionStatus提交事务
3.void rollback(TransactionStatus status) throws TransactionException;
通过TransactionStatus回滚事务 - spring事务的状态->TransactionDefinition
可以看到这个类里面定义了上面说过的传播机制,还有隔离级别,还有五个方法:int getPropagationBehavior();
获取事务的传播属性int getIsolationLevel();
获取事务的隔离级别int getTimeout();
获取事务超时时间boolean isReadOnly();
是否只读String getName();
获取事务的名字
再列出来这个类中的隔离机制的描述:这个描述我简化写了,除了第一个,剩下的和上面第二小节的意义差不多,可以翻上去看看
事务隔离级别 | 描述 |
---|---|
ISOLATION_DEFAULT | 和使用的数据库保持一致的隔离机制 |
ISOLATION_READ_UNCOMMITTED | 读取未提交内容 |
ISOLATION_READ_COMMITTED | 读取提交内容 |
ISOLATION_REPEATABLE_READ | 可重读 |
ISOLATION_SERIALIZABLE | 串行 |
- spring事务状态-> TransactionStatus
然后介绍一下这六个方法的作用:void flush();
用于刷新底层会话中的修改到数据库,一般用于刷新如Hibernate/JPA的会话,可能对如JDBC类型的事务无任何影响boolean hasSavepoint();
返回当前事务是否有保存点,保存点的意思在嵌套事务中的存盘点/保存点,两个事物嵌套的时候会有这个点boolean isCompleted();
当前事务否已经完成boolean isNewTransaction();
是否是一个新的事务boolean isRollbackOnly();
返回事务是否为仅回滚void setRollbackOnly();
设置当前事务只能回滚
六、spring事务的实现
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Service
public class TestServcieImpl {
@Autowired
PlatformTransactionManager platformTransactionManager;//注入事务管理器
public void test(){
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();//创建默认的TransactionDefinition
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//传播机制
TransactionStatus status = platformTransactionManager.getTransaction(definition);//根据TransactionDefinition获取事务 返回结果是TransactionStatus
try{
//处理业务代码.....
platformTransactionManager.commit(status);//提交事务
} catch(Exception e){
platformTransactionManager.rollback(status);//回滚事务
}
}
}
这是使用纯代码的形式使用事务,一般我们在项目中使用的时候,不会这么写,因为太麻烦了,在spring中使用AOP和代理模式来处理事务管理,这个也是很重要的知识,这里我就不详细解释代理模式了,本篇主要讲事务,以后有机会会写代理模式和AOP的资料。
在springMVC中我们一般使用声明式容器事务管理或者注解式事务管理,这个大家应该都知道,简单说一下:
下面这个一般在配置文件中,是 声明式容器事务管理
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" />
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
还有下面这个是 注解式事务管理
<tx:annotation-driven transaction-manager="transactionManager"/>
至于这两种代码中怎么写,大家应该都用过,这里就不举例子了,不清楚的可以去查查资料
好了,就说到这里。个人浅薄理解,欢迎补充