探索MySQL事务的奥秘:事务隔离级别、事务不生效原因详解

本文详细讲解了MySQL事务的概念、隔离级别、脏读/不可重复读/幻读问题,以及如何在SpringBoot中配置和管理事务,包括事务注解、传播机制和可能导致事务不生效的常见原因。
摘要由CSDN通过智能技术生成

探索MySQL事务的奥秘:事务隔离级别、事务不生效原因详解

MySQL基础知识:介绍MySQL数据库的基本概念和常用命令,如何创建数据库、表、用户和权限管理等。
MySQL安装教程:Centos7 安装MySQL5.7.29详细安装手册

MySQL数据类型:详细介绍MySQL支持的各种数据类型,如整型、字符型、日期时间型等,以及它们的存储和使用方法。

MySQL查询语句:详解MySQL查询语句:SELECT语句一网打尽

MySQL事务管理:探索MySQL事务的奥秘:事务隔离级别、事务不生效原因详解。

MySQL性能优化:解锁MySQL性能的魔法:优化技巧大揭秘包括索引优化、失效原因、缓存策略、分区表等

MySQL高可用性:MySQL高可用性攻略:快速搭建MySQL主从复制集群 !

MySQL高频面试题大揭秘:通关攻略,涵盖丰富题型,面试必备!


前言

MySQL作为最流行的关系型数据库之一,其事务支持功能十分强大,本文将深入探讨MySQL中事务的隔离级别、事务不生效原因。

在这里插入图片描述


一、什么是事务?

事务是数据库中一系列操作的集合,这些操作要么全部成功执行,要么全部失败回滚。事务的四个关键特性通常被缩写为ACID:

  • 原子性(Atomicity):事务中的所有操作要么全部成功执行,要么全部失败回滚,不存在部分执行的情况。
  • 一致性(Consistency):事务在执行前后,数据库的状态保持一致性。即使事务失败,也不会破坏数据库的完整性约束。
  • 隔离性(Isolation):多个事务同时执行时,每个事务都感觉不到其他事务的存在,即每个事务都是独立的。
  • 持久性(Durability):一旦事务提交成功,对数据库的修改就会永久保存,即使系统崩溃也不会丢失。

二、事务的隔离级别有哪几种

介绍之前先说明下什么是脏读、不可重复读和幻读,它们是数据库中常见的并发问题,它们可能会影响事务的一致性。以下是对这三种问题的介绍:

  1. 脏读指的是一个事务读取了另一个事务未提交的数据。例如,事务A读取了事务B正在修改但尚未提交的数据,如果事务B回滚了,则事务A读取到的数据就是无效的,因此称为脏数据。
    脏读问题会导致读取到不一致的数据,可能会产生错误的结果或不符合预期的行为。
    不可重复读(Non-Repeatable Read):

  2. 不可重复读指的是在一个事务中,同一条记录在事务执行期间被另一个事务修改,导致多次读取同一条记录时得到的结果不一致。
    例如,事务A首先读取了一条记录,然后事务B修改了该记录并提交,随后事务A再次读取同一条记录,发现其值已经发生了变化,这就是不可重复读问题。
    不可重复读问题可能会导致事务的逻辑错误,破坏了事务的隔离性。
    幻读(Phantom Read):

  3. 幻读指的是在一个事务中,同一查询条件下的结果集在事务执行期间被另一个事务插入或删除,导致多次查询同一数据集时得到的结果集不一致。
    例如,事务A首先查询了一个范围内的数据,并对结果集进行操作,然后事务B插入了符合查询条件的新数据,接着事务A再次查询同一范围内的数据,发现结果集中出现了新插入的数据,这就是幻读问题。
    幻读问题可能会导致事务的结果不可预测,破坏了事务的隔离性。

数据库事务的隔离级别是指多个事务之间的可见性和并发性的控制程度。MySQL支持四种标准的隔离级别,每种级别都具有不同的特性和影响。这些隔离级别包括:

  1. 读未提交(Read Uncommitted):
  • 场景:假设有一个银行转账的例子,A用户正在向B用户转账,此时B用户的余额尚未更新。
  • 行为:在读未提交的隔离级别下,即使A用户的转账事务尚未提交,其他事务也可以读取到B用户的未提交的余额。这可能导致其他事务读取到不一致的数据,产生脏读问题。
  1. 读已提交(Read Committed):
  • 场景:在同样的银行转账例子中,但是在读已提交的隔离级别下。
  • 行为:在读已提交的隔离级别下,其他事务只能读取到已经提交的数据,因此即使A用户的转账事务尚未提交,其他事务也无法读取到B用户的未提交的余额。这可以避免脏读,但可能导致不可重复读和幻读问题。
  1. 可重复读(Repeatable Read):
  • 场景:继续以银行转账为例,A用户正在向B用户转账,此时B用户的余额尚未更新。
  • 行为:在可重复读的隔离级别下,其他事务无法读取到A用户正在转账的数据,也就是说,即使A用户的转账事务尚未提交,其他事务也无法读取到B用户的未提交的余额。这可以避免脏读和不可重复读,但仍然可能出现幻读问题,例如在查询某个范围内的数据时,其他事务插入新数据可能导致结果集发生变化。
  1. 串行化(Serializable):
  • 场景:在同样的银行转账例子中,但是在串行化的隔离级别下。
  • 行为:在串行化的隔离级别下,事务会被序列化执行,即使同时有多个事务试图访问同一数据,也只有一个事务可以访问。这可以完全避免脏读、不可重复读和幻读等问题,但会降低数据库的并发性能。

三、如何在Spring Boot使用MySQL事务

1. 配置事务注解

Spring Boot提供了简单易用的方式来管理数据库事务。通过@Transactional注解,可以轻松地在Spring Boot应用中实现事务管理。例如:

import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void updateUser(User user) {
        // 更新用户信息
        userRepository.save(user);
    }

    // 其他业务方法
}

在以上示例中,@Transactional注解被应用在updateUser方法上,表示该方法应该在一个事务中执行。如果方法成功执行,则事务将被提交;如果方法出现异常,则事务将被回滚,数据库恢复到方法执行前的状态。
不过一般我们会配上spring事务的传播机制和回滚异常类
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)

2.spring事务的传播机制

名称介绍
PROPAGATION_REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

3.代码中事务不生效的原因

当编写代码时,也有一些情况可能会导致事务不生效,以下是一些常见的情况:

  1. 未在Spring Bean方法中调用事务方法:在Spring中,只有通过代理调用的方法才会被事务管理器拦截,从而启用事务。如果在同一个类中调用一个带有@Transactional注解的方法,但是没有通过代理调用该方法,事务将不会生效。
@Service
public class MyService {
    
    @Autowired
    private MyRepository myRepository;
    
    @Transactional
    public void doSomething() {
        // 这里的事务生效
        myRepository.save(something);
        
        // 这里的事务不生效
        doAnotherThing(); // 没有通过代理调用该方法
    }
    
    public void doAnotherThing() {
        // ...
    }
}
  1. 在同一个类中使用self-invocation:如果在同一个类中的一个@Transactional方法中调用另一个@Transactional方法,Spring事务管理器将无法拦截该调用,导致事务不生效。这是因为Spring使用基于代理的AOP来管理事务,而基于代理的AOP对self-invocation的调用不会生效。
@Service
public class MyService {
    
    @Autowired
    private MyRepository myRepository;
    
    @Transactional
    public void doSomething() {
        // 这里的事务生效
        myRepository.save(something);
        
        // 这里的事务不生效
        doAnotherThing(); // self-invocation
    }
    
    @Transactional
    public void doAnotherThing() {
        // ...
    }
}

3. 捕获异常并不重新抛出:如果在事务方法中捕获了异常但没有重新抛出,事务将不会回滚。因为Spring事务管理器默认只捕获未被捕获的异常来触发事务回滚。

@Transactional
public void doSomething() {
    try {
        // 事务生效
        // 一些操作
    } catch (Exception e) {
        // 异常被捕获,但不重新抛出
        // 事务不会回滚
    }
}

4. 方法调用时使用了线程池:如果在使用线程池的方法中调用了带有@Transactional注解的方法,事务将不会生效。这是因为Spring的事务管理器是基于线程本地变量实现的,当线程池中的线程复用时,事务上下文无法正确传播。

5. 非公共方法调用:如果在同一个类中调用了一个带有@Transactional注解的非公共方法,事务将不会生效。因为Spring AOP是基于代理的,默认情况下只能拦截公共方法。

@Service
public class MyService {
    
    @Autowired
    private MyRepository myRepository;
    
    @Transactional
    public void doSomething() {
        // 这里的事务生效
        myRepository.save(something);
        
        // 这里的事务不生效
        doAnotherThing(); // 非公共方法
    }
    
    @Transactional
    private void doAnotherThing() {
        // ...
    }
}

以上是一些可能导致事务不生效的常见代码编写情况。在使用事务时,务必要注意这些问题,确保事务能够正确生效,以保障数据的一致性和完整性。

四、总结

本篇文章介绍到这里就结束了,希望对您有所帮助,另外有什么问题可以在评论区进行交流。

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL事务可以失效的几个场景包括: 1. 隐式提交:如果在一个事务还未提交的情况下,开启了一个新的事务,那么前一个事务会隐式提交,导致事务失效。这是因为MySQL默认使用自动提交模式,每个SQL语句都被视为一个独立的事务,如果没有明确使用BEGIN或START TRANSACTION语句来开启事务,那么每个SQL语句都会被隐式提交。 2. 并发操作:在并发的情况下,多个事务同时进行读写操作时,可能出现幻读、不可重复读等问题,从而导致事务的隔离性受到干扰。例如,当一个事务在读取某个数据的同时,另一个事务对该数据进行了修改,那么前一个事务可能无法得到一致的数据,从而导致事务失效。 3. 锁操作:在使用锁进行并发控制时,如果锁的粒度不当或者锁的使用方式不正确,可能导致事务失效。例如,当一个事务在读取某个数据的同时,另一个事务对该数据进行了锁定,那么前一个事务可能无法继续执行,从而导致事务失效。 需要注意的是,MySQL事务失效并不是指整个事务都无效,而是指某些操作或某些隔离性可能受到干扰,导致事务无法达到预期的结果。为了确保事务的有效性,建议合理使用事务隔离级别、锁机制,并注意事务的提交和回滚操作。 参考资料: MySQL 5.7 Reference Manual - 15.3.2 事务控制语句:https://dev.mysql.com/doc/refman/5.7/en/transaction-control-statements.html MySQL 5.7 Reference Manual - 13.2.1 InnoDB 事务隔离级别:https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值