总结篇 | 详聊事务

本文介绍事务相关的概念,在数据库、spring中的事务及管理,以及分布式事务的解决方案,还有部分内容需要细化。

数据库事务

在MySQL的引擎里,MyISAM 不支持事务,以下均是基于 innodb 引擎展开。

事务定义

定义:数据库系统中一系列操作的逻辑单元,所有操作要么全部成功要么全部失败
事务特性(ACID):

  • 原子性(atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性(consistency):从一个一致性状态变换到另一个一致性状态,即一个事务执行之前和执行之后都必须处于一致性状态。
  • 隔离性(isolation):数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
  • 持久性(durability):一个事务一旦被提交,则对数据库的所有更新将被保存到数据库中,不能回滚。

并发事务会带来哪些问题

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一 数据进行操作)。并发虽然是必须的,但可能会导致以下的问题:

  • 更新丢失: 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。
  • 脏读: 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。
  • 不可重复读: 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。
  • 幻读:幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。

并发事务带来很多问题,为了不同程度上避免这些问题,数据库设置了不同的事务合理级别。

事务隔离级别

读未提交 Read uncommitted :能够读取到没有被提交的数据。
读已提交 Read committed:即能够读到那些已经提交的数据。
可重复读 Repeatable read:读取了一条数据,这个事务不结束,别的事务就不可以改这条记录。
序列化Serializable:最高的事务隔离级别,不管多少事务,序列化执行
Spring事务

这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

√: 可能出现 ×: 不会出现
在这里插入图片描述

事务的实现原理

事务的隔离性由锁来实现。原子性,一致性,持久性通过数据库的redo log 和 undo log 来完成。redo log 称为重做日志,用来保证事务的原子性和持久性。
一致性:undo log
原子性:undo log , redo log
持久性:redo log, undo log
隔离性:mvcc ,锁

ACID中的AID都是为了实现C (一致性)的。事务的最终目的就是为了实现“一致性”。
在MySQL里:
原子性:undolog ,通过undolog保证,如果失败可以回滚
隔离性:MVCC,多版本并发控制,锁
持久性:redolog,(两阶段提交,WAL Write ahead log,先写日志再写数据)

redo log

redo 由两部分组成,一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file)其是持久的。

InnoDB是事务的存储引擎,其通过Force Log at Commit机制实现事务的持久
性,即当事务提交(COMMIT)时,必须先将该事务的所有日志写人到重做日志文件
进行持久化,待事务的COMMIT操作完成才算完成
。这里的日志是指重做日志,在
InnoDB存储引擎中,由两部分组成,即redo log和undo log。redo log用来保证事务的
持久性,undo log用来帮助事务回滚及MVCC的功能。redo log基本上都是顺序写的,
在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读
写的。
为了确保每次日志都写入重做日志文件,在每次将重做日志缓冲写人重做日志文件
后,InnoDB存储引擎都需要调用一次fsync操作。重做日志缓冲先写人文件系统缓存。

undo

undo 是逻辑日志,有两个主要的作用 : 回滚和MVCC

1)回滚。将数据库逻辑的恢复到原来的样子,例如 对于每个insert, innodb 存储引擎会完成一个delete;
2)MVCC。即在innodb 存储引擎中,MVCC的实现是通过undo来完成的。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。
还有一点比较重要的是,undo log 会产生redo log ,也就是 undo log 的产生会伴随这redo log 的产生,这是因为 undo log 也需要持久性的保护。

MVCC 实现原理

MVCC(Multi-Version Concurrency Control | 多版本并发控制)

// TODO

readView 读取

// TODO

Spring事务

1、Spring编程式事务

2、Spring声明式事务Spring事务失效问题

Spring 的声明式事务管理是建立在 Spring AOP 机制之上的,其本质是对目标方法前后进行拦截,并在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务

  • 基于 命名空间的声明式事务管理
    Spring 2.x 引入了 命名空间,结合使用 命名空间,带给开发人员配置声明式事务的全新体验,配置变得更加简单和灵活。总的来说,开发者只需基于和命名空间在XML中进行简答配置便可实现声明式事务管理。
  • 基于 @Transactional 的声明式事务管理  
    除了基于命名空间的事务配置方式,Spring 还引入了基于 Annotation 的方式,具体主要涉及@Transactional 标注。@Transactional 可以作用于接口、接口方法、类以及类方法上:当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性;当作用于方法上时,该标注来覆盖类级别的定义。

事务传播行为

所谓spring事务的传播属性,就是定义在存在多个事务同时存在的时候,spring应该如何处理这些事务的行为。
在这里插入图片描述

Spring事务实现原理

//TODO:详细补充
1.PlatformTransactionManager:平台事务管理器,主要是用于管理事务。
2.TransactionDefinition :事务定义的对象,该对象中定义了一系列基本的事务属性,并提供了获取事务相关的信息的方法。
3.TransactionStatus:事务的状态,它描叙了某一时间点上事务的状态信息。
在这里插入图片描述
在这里插入图片描述

Spring事务常见失效问题

1. 数据库存储引擎不支持。
 mySQL的myISAM存储引擎就不支持事务功能。
2. 没有被spring 管理

如下面例子所示:

// @Service
public class OrderServiceImpl implements OrderService {
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
}

如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。

3. 方法不是 public 的

以下来自 Spring 官方文档:

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

大概意思就是 @Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

4. 自身调用问题

来看两个示例:

@Service
public class OrderServiceImpl implements OrderService {

    public void update(Order order) {
        updateOrder(order);
    }

    @Transactional
    public void updateOrder(Order order) {
        // update order
    }

}

update方法上面没有加 @Transactional 注解,调用有 @Transactional 注解的 updateOrder 方法,updateOrder 方法上的事务管用吗?
再来看下面这个例子:

@Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void update(Order order) {
        updateOrder(order);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateOrder(Order order) {
        // update order
    }

}

这次在 update 方法上加了 @Transactional,updateOrder 加了 REQUIRES_NEW 新开启一个事务,那么新开的事务管用么?

这两个例子都是:不管用

因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。有办法么,也有,大家可以自行查阅相关的解决办法。

5. 异常被吃了
// @Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {

        }
    }

}
6. 异常类型错误
// @Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
            throw new Exception("更新错误");
        }
    }
}

这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:

@Transactional(rollbackFor = Exception.class)
这个配置仅限于 Throwable 异常类及其子类。

分布式事务

分布式事务场景:一个服务一个数据库,比如:用户服务对应一个用户数据库,商品服务对应一个商品数据库,订单服务对应一个订单数据库,这就是微服务的产生,微服务之间通过远程调用完成事务操作,这就产生了分布式事务。

问题及解决方案

理论

在分布式系统中,难免会出现网络以及服务故障等,如果保证分布式系统的事务。通常情况下为达到良好的响应性能提升用户体验,通常会选择CAP理论中AP组合,舍弃C强一致性,保证最终一致性。首先来回顾一下CAP 理论:

CAP:指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
  • 可用性(A):保证每个请求不管成功或者失败都有响应。
  • 分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。

总体方案

在这里插入图片描述

分布式事务解决方案详解

// TODO详解及线上应用案例

说明:本文是在总结过程,参考很多个人分享以及网上资料进行整理汇总整理。链接比较零碎,不一一列举了。如侵删。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值