Mysql事务级别和Spring事务使用详细介绍总结
admin
撰写于 2025年 03月 18 日

<!--markdown-->## 概念定义

事务(Transaction)是指一个对数据库进行一系列操作集合的统称,它是数据库执行的逻辑单元。在一个逻辑单元中可以有多个CRUD执行操作。

事务特性

事务具有四种特性,一般简称ACID特性

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库

事务作用

为什么要使用事务?由于我们的业务逻辑都是由很多数据组成,每组数据的正确性依赖于其他数据的正确性,完整性,如A向B转账,如果A扣款成功,B一定要收到钱,反之B收到钱,A必须扣款,如果扣款失败,A既不扣款,B也不收款,如扣款成功,A必须扣款,B必须收款。这种业务逻辑的正确执行才能满足我们的实际需求,但是如何去保证这一逻辑的正确执行呢? 这时候就需要用到数据库事务,使用事务可以保证我们的业务逻辑正常执行,数据得到正确性,完整性,安全性。如果没有事物的话数据的完整性一致性等无法保障,我们就无法编写正确的业务逻辑。

事务原理

事务使用

  • 编程式事务:就是直接在代码里手动开启事务,手动提交,手动回滚。优点就是可以灵活控制,缺点就是太麻烦了,太多重复的代码了。
  • 声明式事务:就是使用SpringAop配置事务,这种方式大大的简化了编码。需要注意的是切入点表达式一定要写正确。
  • 注解事务:直接在Service层的方法上面加上@Transactional注解

事务回滚

在多个crud执行中,由于数据的不正确或者异常的导致,我们对数据库提交的数据需要做撤回操作,可以利用数据库事务回滚机制来解决脏数据插入到数据库。使用事务回滚,数据库会将数据恢复到原来之前的样子,确保事务的一致性,原子性。

Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked

如果遇到checked意外就不回滚。

如何改变默认规则:

  1. 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
  2. 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
  3. 不需要事务管理的(只查询的)方法:@Transactional(readOnly=true) 或者不加注解
  4. 如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error

注意:

如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛

try{}catch{throw Exception}

为什么事务回滚默认指是针对未检查异常?

  Checked Exception异常,这也是在编程中使用最多的Exception,所有继承自Exception并且不是RuntimeException的异常都是checked Exception,下图中的IOException和ClassNotFoundException。JAVA 语言规定必须对checked Exception作处理,编译器会对此作检查,要么在方法体中声明抛出checked Exception,要么使用catch语句捕获checked Exception进行处理,不然不能通过编译。Spring鼓励你手动去处理处理这种异常,确保不会因此出现问题。

事务回滚总结:

  1. 要想事务起作用,必须是主方法名上有@Transactional注解,方法体内不能用try catch;如果用try catch,则catch中必须用throw new Exception();
  2. @Transactional注解应该只被应用到public方法上,不要用在protected、private等方法上,即使用了也将被忽略,不起作用。这是由Spring AOP决定的
  3. 只有来自外部的方法调用才会被AOP代理捕捉,类内部方法调用类内部的其他方法,子方法并会不引起事务行为,即使被调用的方法上使用有@Transactional注解。

关于第三点:

在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务。其背后原因是因为事务的实现机制是spring通过代理实现的,spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了。

当然解决办法有两种:

  1. 把这两个方法分开到不同的类中;
  2. 使用代理对象调用当前类的方法

    ServiceA proxy =(ServiceA)AopContext.currentProxy();
    proxy.b();


事务隔离级别

四种事务级别:

  1. 读未提交(read uncommitted ):别人改数据的事务尚未提交,我在我的事务中也能读到。
  2. 读已提交(read committed):别人改数据的事务已经提交,我在我的事务中才能读到。
  3. 可重复读(repeatable read):别人改数据的事务已经提交,我在我的事务中也不去读。
  4. 串行提交(serializable:):事务串行化执行,后面的事务等待前面的事务执行完才能去接着执行。

mysql默认的事务隔离级别是:可重复读(repeatable read)
总结:这 4 种隔离级别,并行性能依次降低,安全性依次提高。

avatar

修改事务级别

查询当前数据库的事务隔离级别:SELECT @@tx_isolation

mysql内置四种事务:

read uncommitted

read committed

repeatable read

serializable

设置当前会话
set session transaction isolation LEVEL read uncommitted

设置全局
set global transaction isolation LEVEL read uncommitted

事务传播机制

在当前含有事务方法内部调用其他的方法(无论该方法是否含有事务),这就属于Spring事务传播机制的知识点范畴了。Spring事务基于Spring AOP

Spring的@Transactional默认是PROPAGATION_REQUIRED。

事务嵌套

事务与锁

  1. 共享锁(S锁)

    用于只读操作(SELECT),锁定共享的资源。共享锁不会阻止其他用户读,但是阻止其他的用户写和修改。

  2. 更新锁(U锁)

    用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。

  3. 独占锁(X锁,也叫排他锁)

    一次只能有一个独占锁用在一个资源上,并且阻止其他所有的锁包括共享缩。写是独占锁,可以有效的防止“脏读”。

Read Uncommited 如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。

Read Committed 读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。可以通过“瞬间共享读锁”和“排他写锁”实现。

Repeatable Read 读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。可以通过“共享读锁”和“排他写锁”实现。

Serializable 读加共享锁,写加排他锁,读写互斥。

Mysql事务级别和Spring事务使用详细介绍总结

<!--markdown-->## 概念定义

事务(Transaction)是指一个对数据库进行一系列操作集合的统称,它是数据库执行的逻辑单元。在一个逻辑单元中可以有多个CRUD执行操作。

事务特性

事务具有四种特性,一般简称ACID特性

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库

事务作用

为什么要使用事务?由于我们的业务逻辑都是由很多数据组成,每组数据的正确性依赖于其他数据的正确性,完整性,如A向B转账,如果A扣款成功,B一定要收到钱,反之B收到钱,A必须扣款,如果扣款失败,A既不扣款,B也不收款,如扣款成功,A必须扣款,B必须收款。这种业务逻辑的正确执行才能满足我们的实际需求,但是如何去保证这一逻辑的正确执行呢? 这时候就需要用到数据库事务,使用事务可以保证我们的业务逻辑正常执行,数据得到正确性,完整性,安全性。如果没有事物的话数据的完整性一致性等无法保障,我们就无法编写正确的业务逻辑。

事务原理

事务使用

  • 编程式事务:就是直接在代码里手动开启事务,手动提交,手动回滚。优点就是可以灵活控制,缺点就是太麻烦了,太多重复的代码了。
  • 声明式事务:就是使用SpringAop配置事务,这种方式大大的简化了编码。需要注意的是切入点表达式一定要写正确。
  • 注解事务:直接在Service层的方法上面加上@Transactional注解

事务回滚

在多个crud执行中,由于数据的不正确或者异常的导致,我们对数据库提交的数据需要做撤回操作,可以利用数据库事务回滚机制来解决脏数据插入到数据库。使用事务回滚,数据库会将数据恢复到原来之前的样子,确保事务的一致性,原子性。

Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked

如果遇到checked意外就不回滚。

如何改变默认规则:

  1. 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
  2. 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
  3. 不需要事务管理的(只查询的)方法:@Transactional(readOnly=true) 或者不加注解
  4. 如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error

注意:

如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛

try{}catch{throw Exception}

为什么事务回滚默认指是针对未检查异常?

  Checked Exception异常,这也是在编程中使用最多的Exception,所有继承自Exception并且不是RuntimeException的异常都是checked Exception,下图中的IOException和ClassNotFoundException。JAVA 语言规定必须对checked Exception作处理,编译器会对此作检查,要么在方法体中声明抛出checked Exception,要么使用catch语句捕获checked Exception进行处理,不然不能通过编译。Spring鼓励你手动去处理处理这种异常,确保不会因此出现问题。

事务回滚总结:

  1. 要想事务起作用,必须是主方法名上有@Transactional注解,方法体内不能用try catch;如果用try catch,则catch中必须用throw new Exception();
  2. @Transactional注解应该只被应用到public方法上,不要用在protected、private等方法上,即使用了也将被忽略,不起作用。这是由Spring AOP决定的
  3. 只有来自外部的方法调用才会被AOP代理捕捉,类内部方法调用类内部的其他方法,子方法并会不引起事务行为,即使被调用的方法上使用有@Transactional注解。

关于第三点:

在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务。其背后原因是因为事务的实现机制是spring通过代理实现的,spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了。

当然解决办法有两种:

  1. 把这两个方法分开到不同的类中;
  2. 使用代理对象调用当前类的方法

    ServiceA proxy =(ServiceA)AopContext.currentProxy();
    proxy.b();


事务隔离级别

四种事务级别:

  1. 读未提交(read uncommitted ):别人改数据的事务尚未提交,我在我的事务中也能读到。
  2. 读已提交(read committed):别人改数据的事务已经提交,我在我的事务中才能读到。
  3. 可重复读(repeatable read):别人改数据的事务已经提交,我在我的事务中也不去读。
  4. 串行提交(serializable:):事务串行化执行,后面的事务等待前面的事务执行完才能去接着执行。

mysql默认的事务隔离级别是:可重复读(repeatable read)
总结:这 4 种隔离级别,并行性能依次降低,安全性依次提高。

avatar

修改事务级别

查询当前数据库的事务隔离级别:SELECT @@tx_isolation

mysql内置四种事务:

read uncommitted

read committed

repeatable read

serializable

设置当前会话
set session transaction isolation LEVEL read uncommitted

设置全局
set global transaction isolation LEVEL read uncommitted

事务传播机制

在当前含有事务方法内部调用其他的方法(无论该方法是否含有事务),这就属于Spring事务传播机制的知识点范畴了。Spring事务基于Spring AOP

Spring的@Transactional默认是PROPAGATION_REQUIRED。

事务嵌套

事务与锁

  1. 共享锁(S锁)

    用于只读操作(SELECT),锁定共享的资源。共享锁不会阻止其他用户读,但是阻止其他的用户写和修改。

  2. 更新锁(U锁)

    用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。

  3. 独占锁(X锁,也叫排他锁)

    一次只能有一个独占锁用在一个资源上,并且阻止其他所有的锁包括共享缩。写是独占锁,可以有效的防止“脏读”。

Read Uncommited 如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。

Read Committed 读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。可以通过“瞬间共享读锁”和“排他写锁”实现。

Repeatable Read 读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。可以通过“共享读锁”和“排他写锁”实现。

Serializable 读加共享锁,写加排他锁,读写互斥。

赞 (0)

评论区(暂无评论)

这里空空如也,快来评论吧~

我要评论