云计算、AI、云原生、大数据等一站式技术学习平台

网站首页 > 教程文章 正文

MySQL事务篇全网最深入的一篇讲解

jxf315 2025-05-11 16:21:25 教程文章 23 ℃
  • 事务定义
  • 事务特性ACID
  • 事务隔离级别
  • spring事务属性
  • spring声明式事务配置

1 事务及事务特性(ACID)

作为一个程序员,相信我们一定已经接触了许多关于事务的话题,事务主要是针对数据库中的数据的增删改查操作,数据库又是如何管理事务的呢?感兴趣的同学可以自己查阅相关资料,例如Mysql中的MVVC机制是如何保证事务的隔离性?

事务是什么?事务是由一系列SQL语句组成的完成某一业务功能的基本单元,他好比一个存储过程,一个函数,由若干SQL语句构成的为了完成某一项特点功能而组成一个代码块。数据库事务要保证当insert或update操作时抛异常或者数据库crash的时候需要保障数据的操作前后的一致,为数据提供可靠性保障。事务它具备四个基本特性,了解这四个特性是对事务的初步认识。

原子性(Atormicity):一个事务中的SQL语句要么全部执行(commit),要么全部不执行(rollback

一致性(Consistency):所谓一致性最为难理解,指事务完成后,一个业务模型从一个一致性状态转换到另一个一致性状态,此处使用物理学能量守恒定理理解更为合适,可以理解为事务完成后从一个业务模型转换到另一个业务模型,相关数据会发生改变,但是不会消失,好比能量守恒定理,动能减少了,必定会转换为其他能量。再通俗点讲,你的账户减少100块钱,那么别的账户必定会增加100块钱,你的100块钱不可能平白无故消失。(一致性由其他三个特性保证,通过回滚,恢复,及在并发环境下通过隔离实现事务的一致性

隔离性(Isolation):两个事务之间互相隔离(描述的是一个事务的修改操作相对于其他事务的可见性,事务隔离性中定义的每种事务隔离级别,就定义了一个事务的修改,哪些事务可见,哪些事务不可见)

持久性(Durability):事务一旦提交之后就会持久化到数据库持久层,不能改变,或许MySQL服务器经历宕机,停电,重启之后,事务提交的数据然仍会被持久化的磁盘上。

2.Spring事务核心接口:了解spring核心接口便于深入理解spring事务底层实现机制

  • TransactionDefinition:事务的定义,交代了事务的属性,事务的方法
  • PlatformTransactionManager:spring事务管理器顶层接口,DataSourceTransactionManager,HibernateTransactionManager,JpaTransacationManager,JtaTransactionManager为对应平台相关的事务实现,他们底层都是对JDBC事务管理进行封装。mybatis使用的是DataSourceTransactionManager
  • TransactionStatus:表示事务状态,事务具有如下状态:rollback,commit,avepoint...

2.1 事务基本属性

spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring事务管理器的接口是
org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。相关平台对应的事务管理器,请各位同学自行了解。

2.2 sring事务属性,从事务定义接口中获取事务属性

public interface TransactionDefinition {
    int getPropagationBehavior(); // 返回事务的传播行为
    int getIsolationLevel(); // 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
    int getTimeout();  // 返回事务必须在多少秒内完成
    boolean isReadOnly(); // 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
} 

2.2.1 事务传播行为

传播行为

含义

PROPAGATION_REQUIRED

表示当前方法必须运行在事务当中,如果当前方法存在事务,就运行在该事务当中,否则,创建一个新的事务

PROPAGATION_SUPPORTS

表示该前方法支持事务,如果存在事务,就运行在该事务中,如果存在事务,也可以运行

PROPAGATION_MANDATORY

表示强制该方法必须运行在事务当中,如果存在事务就运行在该事务中,如果不存在事务抛出异常

PROPAGATION_REQUIRE_NEW

表示当前方法必须运行在一个新的事务当中,如果存在事务,那么当前事务先挂起,创建新的事务,执行方法,再开启原有的事务

PROPAGATION_NOT_SUPPORT

表示当前方法不支持事务,如果存在事务,那么事务先挂起,再执行方法

PROPAGATION_NEVER

表示当前方法强烈反对事务,如果存在事务直接报错

PROPAGATION_NESTED

表示嵌套事务,如果存在其他事务,与嵌套事务不相干,每个事务独立运行

2.2.2 隔离级别

事务的第二个维度就是隔离级别(isolation level)。隔离级别定义了一个事务可能受其他并发事务影响的程度(一个事务的修改,在哪些事务中是可见的,在哪些事务时不可见)。 在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致一下的问题。

  • 脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的
  • 不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
  • 幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。

隔离级别

定义

ISOLATION_DFAULT

使用后端数据库默认的隔离级别(MySQL--Repetable Read)

ISOLATION_READ_UNCOMMITTED

最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读,一个事务的修改,即使没有提交,对于其他事务也是可见的。

ISOLATION_READ_COMMITTED

允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生,一个事务在他提交之前的所有修改,对于其他事务是不可见的

ISOLATION_REPETABLE_READ

对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生,一个事务内的多次读取结果是一样的

ISOLATION_SERALIZABLE

最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的(串行,效率低,不可取

2.2.3 只读

事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。

2.2.4 事务超时

为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

2.2.5 回滚规则

默认情况下只有异常(RuntimeException和Error类型的异常)会导致事务回滚。事务的回滚规则可以通过属性管理

rollbackFor:遇到时必须进行回滚

noRollbackFor:一组异常类,遇到时必须不能回滚

//了解事务属性是非常有必要的,因为在不同的业务场景中事务的处理方式是不一样的。无论是在
//XML配置还是注解方式配置事务,都需要针对不同的方法配置相应的事务属性。事务属性配置如下:

============================XML配置===========================
//配置事务管理器,无论哪种方式都需要配置事务管理器,事务都是由事务管理器创建
  <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
//为相应的方法配置事务属性,使用springAOP增强的方式配置属性
<!--事务属性配置-->
    <tx:advice id="txAttribute" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="select*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1"/>
            <tx:method name="transfer*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
        </tx:attributes>
    </tx:advice>
//通过SpringAOP将相应方法的事务属性增强织入到相应的切入点中

=======================@Transactional============================
 <!-- 启用事务注解 -->
   //启动事务注解扫描
	<tx:annotation-driven transaction-manager="transactionManager"/>
    //在相应的方法上配置事务
    //添加事务注解
	//1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
	//如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
	//REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起. 
	//2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
	//3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
	//属性进行设置. 通常情况下去默认值即可. 
	//4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据, 
	//这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
	//5.使用 timeout 指定强制回滚之前事务可以占用的时间.
	@Transactional(propagation=Propagation.REQUIRES_NEW,
			isolation=Isolation.READ_COMMITTED,
			noRollbackFor={UserAccountException.class},
			rollbackFor = IOException.class,
			readOnly=false,
			timeout=3)
	@Override
	public void purchase(String username, String isbn) {}

篇尾寄语:

技术赋能于业务,希望本篇文章让每一位读者有所收益...................................................

如有不当的地方请多多计较,共同学习,共同进步,fighting............................................

祝所有编程爱好者,求职,加薪,迎娶白富美,走向人生巅峰........................................

Tags:

最近发表
标签列表