事务和Seata学习笔记

2023-07-11  本文已影响0人  曹大大

什么是事务?

定义

数据库事务是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。

组成

一个数据库事务通常包含对数据库进行或者的一个操作序列

目的

  1. 为数据库操作提供了一个从失败中恢复正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
  2. 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰

总结

  1. 失败恢复方法
  2. 保持一致性的方法
  3. 操作隔离的方法

成功的情况下

能将数据从一种状态变为另外一种状态,并能够持久化。

异常情况下

  1. 能将数据恢复到正常状态
  2. 要能保证一致性,包含数据的一致性和约束的一致性

并发的情况下

并发的操作之间不能产生相互影响。

事务的特性

事务的并发异常

image-20230227133232830.png image-20230227133341342.png image-20230227133450281.png

事务隔离级别

隔离级别命令

数据库锁

按照性质分类:

MySQL行锁-同一条数据加锁

Innodb的特性

  1. 支持行级锁、表锁,支持事务,支持外键,支持非锁定读(默认读取操作不会产生锁)。
  2. 通过多版本并发控制(MVCC)获得高并发性,并实现SQL标准的4种隔离级别。
  3. 使用Next-key Locking策略避免幻读现象的产生。
  4. 供插入缓冲,二次写,自适应哈希索引,预读高性能和高可用的功能。
image-20230227150321832.png
/*加S锁语句*/
select * from T where id = 1 lock in share mode;
/*加X锁语句*/
select * from T where id =1 for update;
image-20230227150420594.png image-20230227150433420.png image-20230227150456695.png

MySQL行级锁-不同行数据加锁

image-20230227151822730.png image-20230227151858775.png image-20230227151902399.png

MySQL行级锁-全记录加锁

image-20230227152030022.png image-20230227152043115.png

索引与锁的关系

image-20230227152403030.png

MySQL的Innodb是按照索引进行加锁的如果语句没有使用索引,则引起全表扫描,导致全表数据被锁,所以其他事务再想锁定一行没办法锁定了。

间隙锁、Next-key锁

间隙锁

image-20230227153433517.png

间隙锁英文Gap Lock

过程:

(负无穷,11)(11,22)(22,33)(33,44)(44,正无穷)

因此ono=22的数据被加X锁,则(11,22)(22,33)也会被加锁,有效防止了幻读的产生,隔离级别必须为可重复读

image-20230227153648203.png

Next-Key锁

过程:

(负无穷,11)(11,22)(22,33)(33,44)(44,正无穷)

因此ono=22的数据被加X锁,先会给其对应的主键索引加上行锁。然后在(11,22)(22,33)加上间隙锁,有效的放置了幻读的产生,隔离界别必须为可重复读。

死锁

image-20230227155225843.png

查看死锁日志

show engine innodb status;
show engine innodb status\G;

意向锁(表级锁)

表级锁/意向锁

锁简称 全称 说明
IS锁 意向共享锁 告诉事务稍后要向表中的个别记录增加S锁,当对某条数据加S锁的时候,必须先向表上加IS锁。
IX锁 意向排它锁 告诉事务稍后想表中的个别记录增加X锁,当对某条数据加X锁的时候,必须先向表上加IX锁。
S锁 共享锁 LOCK table...READ增加表级S锁
X锁 排它锁 LOCK TABLE...WRITE增加表级X锁

兼容关系

X IX S IS
X 冲突 冲突 冲突 冲突
IX 冲突 兼容 冲突 兼容
S 冲突 冲突 兼容 兼容
IS 冲突 兼容 兼容 兼容

表级锁只会与表级锁冲突,行级锁只会和行级锁冲突。

意向锁的作用

例如向一个表添加表级X锁的时候

表级锁操作

image-20230227162726534.png image-20230227162736687.png image-20230227162739408.png image-20230227162742885.png
  • 表锁:不会出现死锁,发生锁冲突几率高,并发低
  • 行锁:会出现死锁,发生锁冲突几率低,并发高

意向补充锁

补充:

乐观锁的实现侧策

  1. version字段
  2. 时间戳
  3. 待更新字段 Wait Update Column(类似于把字段拼接到sql里进行匹配,跟version差不多)
  4. 所有字段 All Column(所有字段做对比)

MVCC多版本并发控制深度解析一

思考

  1. MySQL是如何保证高并发的?

    隔离级别、锁机制

  2. 多个事务并发读取一条数据需要加锁吗?

    不需要

  3. 一个事务读数据,另外一个事务写数据需要加锁吗?

    当前读需要加锁、快照读不需要加锁

  4. 一个事务在写数据,另外一个事务也在写数据需要加锁吗?

    加锁

基础

  1. MVCC全称是Multi-Version Concurrency Control,即多版本并发控制。
  2. 主要是为了提高数据库的并发性能。
  3. 只有InnoDB引擎支持MVCC。
  4. 只有InnoDB引擎支持事务。

查询方式

  1. 当前读:它读取的数据库记录,都是当前最新的版本,会对当前读取的数据进行加锁,防止其他事务修改数据。是悲观锁的一种操作。
    1. select lock in share mode
    2. select for update
    3. update
    4. insert
    5. delete
    6. 串行化级别的select
  2. 快照读:不加锁的select操作,隔离级别不能是串行化。

MVCC多版本并发控制深度解析二

版本链

数据行的三个隐藏字段

字段 描述
db_trx_id 6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
db_row_id 6byte,隐含的自增ID(隐藏主键),如果数据库表没有主键,InnoDB会自动以db_row_id产生一个聚簇索引
db_roll_pointer(版本链关键) 7byte,回滚指针,指向这条记录的上个版本(存储与rollback segment里)

undolog

Read View

image-20230228144659381.png

幻读问题和解决方案

幻读的现象

在同一个事务中,不同的位置,读取的数据条数不一致。

例子:读取数据有3条,更新数据缺更新了4条

产生的原因

同一个事务的不同的位置,读取方式发生了变化。

如果我们在一个事务当中,既有快照读也有当前读,那么就可能产生幻读问题。

解决方案

如果我们在一个事务当中完全使用快照读或当前读,那么就不会有幻读问题。

一个业务真的能满足上述条件吗?

对于可能会产生幻读的业务,我们应该在更新数据之前,查询数据的时候使用当前读(可以使用加锁进行当前读)。

例子

事务1 事务2
begin begin
select * from user select * from user
insert into... -
commit -
select * from user for update
update user set...

一般使用的就是RR级别,默认隔离级别。

是否所有的事务都要使用当前读?

不确定,需要根据业务来。在RR级别下解决幻读问题,由开发人员决定,而不是由MySQL来决定的。

S串行化全部都是当前读,不会有幻读问题。

Spring事务注解

参数名称 功能描述
readOnly 该属性用于设置当前事务是否是只读事务,设置true表示只读,false则表示可读写,默认false
rollbackFor 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚
rollbackForClassName 该属性用于设置需要进行回滚的异常名称数组,当方法中抛出指定异常名称数组中的异常时,则进行回滚
noRollbackFor 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚
noRollbackForClassName 该属性用于设置不需要进行回滚的异常名称数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚
propagation 该属性用于设置事务的传播行为。例如Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
issolation 该属性用于设置底层数据库事务的隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
timeout 该属性用于设置事务的超时描述,默认为-1表示永不超时

Spring事务的传播行为

传播行为 含义
REQUIRED(默认) 表示当前方法必须在一个具有事务的上下文中运行,如果有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)
SUPPORTS 表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行。
MANDATORY 表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常。
NESTED(需要在不同的Service调用) 表示如果当前方法有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立与被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同PROPAGATION_REQUIRED的一样。注意:这个设置只针对JDBC DataSourceTransactionManager有效,并且必须基于JDBC3.0。
NEVER 表示当方法事务不应该在一个事务中运行,如果存在一个事务,则抛出异常。
REQUIRES_NEW(需要在不同的Service调用) 表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期间被挂起,知道新的事务提交或者回滚才恢复执行。
NOT_SUPPORTED 表示该方法不应该在一个事务中运行。如果有一个事务正在运行,它将在运行期被挂起,直到这个事务提交或回滚才恢复执行

Spring隔离级别

隔离级别 含义
idefault 使用数据库默认的事务隔离级别
read_uncommitted 允许读取尚未提交的修改,可能导致脏读、幻读和不可重复读
read_committed 允许从已经提交的事务读取,可防止脏读、但幻读,不可重复读仍然有可能发生
repeatable_read 对相同字段的多次读取的结果是一致的,除非数据被当前事务自身修改。可防止脏读和不可重复读,但是幻读仍有可能发生
serializable 完全服从acid的原则,确保不发生脏读、不可重复读和幻读,但执行效率最低

分布式事务CAP定理和BASE理论

分布式事务问题

image-20230301154325588.png

情况二和情况三就是我们所需要面临的问题。

CAP原则

又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。CAP原则是NOSQL数据库的基石。

CAP 说明
一致性C 在分布式系统中的所有数据备份,在同一个时刻是否同样的值。(等同于所有的节点访问同一份最新的数据副本)
可用性A 在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容错性P 以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限能达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

BASE理论

是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写。

BASE是对CAP中一致性和可用性的权衡结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的。

核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性

BASE 说明
基本可用BA 基本可用是指分布式系统在出现不可预知的故障的时候,允许损失部分可用性。
软状态S 是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
最终一致性E 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

XA协议

分布式事务解决方案:

两阶段提交型、三阶段提交型、TCC补偿型、异步确保型,最大努力通知型几种解决方案。

image-20230301161357602.png

XA协议由Tuxedo首先提出的,并交给X/Open,做完资源管理器(数据库)与事务管理器的接口标准。目前Oracle、Informix、DB2和Sybase等各大数据库厂家都提供对XA的支持。XA协议才用两阶段提交方式来管理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。

对应JAVA的实现为JTA/JTS,JTA可以说是定义了一套实现XA协议的接口规范,而JTS就是JTA的具体实现。

两阶段提交2PC策略

XA协议包含两阶段提交(2PC)和三阶段提交(3PC)两种实现。

正常流程:

image-20230301163415123.png

协调者:事务管理器

参与者:资源管理器

异常流程:

image-20230301164624219.png

2PC优缺点:

优点:

  1. 强一致性。

缺点:

  1. 同步阻塞问题。导致资源被锁、效率低。
  2. 单节点故障。一旦协调者发生故障则参与者阻塞,或者资源无法释放。
  3. 数据不一致。网络故障等导致部分参与者没有提交事务。
  4. 宕机问题。协调者发出消息后宕机,协调者收到消息后也宕机。

三阶段提交3PC策略

正常状态:

image-20230301165547787.png

第一阶段:

  1. CanCommit,询问每一个参与者,你能不能提交。
  2. 参与者去检查它所有的约束和关联关系是否满足操作这些数据,如果可以操作,则返回OK。
  3. 如果所有的参与者都ok,则进入第二阶段。

第二阶段

  1. 参与者接到PreCommit预提交,就会去记录日志然后锁住所需要的资源。
  2. 操作完成后通知协调者已完成,进入第三阶段。

第三阶段

  1. 协调者发布提交到每一个参与者。
  2. 参与者完成操作后,会给协调者已完成。

异常状态:

情况一:

image-20230302125042416.png

情况二:

image-20230302125146226.png

优缺点:

优点:

  1. 减少阻塞:canCommit可以有效减少阻塞。
  2. 单节点故障:引入超时机制,一旦参与者接收不到协调者的信息则默认提交。

缺点:

  1. 数据不一致:由于超时机制默认提交可能会导致数据不一致。

TCC事务补偿

image-20230302130354057.png
TCC 说明
Try 尝试执行业务。完成所有业务检查(一致性),预留必须业务资源(准隔离性)
Confirm 确认执行业务,不做任何业务检查,只使用Try阶段预留的业务资源
Cancel 取消执行业务释放Try阶段预留的业务资源
主业务服务 主业务服务为整个业务活动的发起方
从业务服务 从业务服务负责提供TCC业务操作,是整个业务活动的操作方。从业务服务不许实现Try、Confirm和Cancel三个接口,供主业务服务调用。由于Confirm和Cancel操作可能被重复调用,故要求Confirm和Cancel两个接口必须是幂等的
业务活动管理器 业务活动管理器管理控制整个业务活动,包括记录维护TCC全局事务的事务状态和每个从业务服务的子事务状态,并在业务活动提交时确认所有的TCC型操作的confirm操作,在业务活动取消时调用所有的TCC操作的cancel操作

TCC实际上把数据库层的二阶段提交上提到应用层来实现,对于数据库来说是一阶段提交,规避了数据库层的2PC性能低下问题,但是TCC操作需要业务实现,开发成本较高。

异步确保型

image-20230302133438148.png

把一个同步的流程改为异步了,中间加入了消息中间件。

优缺点

优点:

  1. 异步高性能:异步处理,不会大面积锁定资源。
  2. 准实时一致性:MQ的准实时特性。
  3. 服务器解耦:MQ自身特性导致。

缺点:

  1. 依赖于MQ事务:多数MQ不支持事务,Rocket支持。
  2. 消费者幂等性:多次请求与一次请求结果相同。

最大努力通知型

image-20230302134743400.png

主动方(支付系统,发起通知的一方),被动方(订单系统,接收通知的一方)

构成

  1. 实时消息服务(MQ或实时接口):接收方主动发送的通知消息。
  2. 通知服务子系统:监听MQ消息、或实时接口消息;当收到消息后,向被动方发送通知,同事生成通知记录。如果没有接收到被动方的返回消息,就根据通知记录进行重复通知。
  3. 提供一个幂等的结果查询接口

可以不把它看作为一种分布式事务解决方案,只认为是一种跨平台的数据处理方案也可以。

SEATA分布式事务框架

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

特色功能

seata的AT、TCC、SAGA模式原理

AT模式

前提

整体机制

两阶段提交协议的演变:

image-20230302150149704.png

TC:事务控制器

TM:事务管理器

RM:资源管理器

TM开启一个全局事务,向TC发送一个begin请求。TC接收到请求后会返回一个XID(本次全局事务的ID),然后各个分支的事务开始做自己的事情了。各个微服务本地的RM都要像TC去注册它自己的分支事务,方便知道提交或者回滚哪些事务。

如果每个事务都已经完成,那么TM要向TC发送请求,通知TC全局事务要提交了。

如果中间某个事务出现问题了,这个时候TM就会告诉TC,出现错误了,我需要回滚,TC会通知各个分支事务进行回滚。

写隔离

读隔离

在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)

TCC 模式

回顾总览中的描述:一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:

image-20230302160559870.png

Saga 模式

Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

image-20230302160829758.png

适用场景:

优势:

缺点:

上一篇 下一篇

猜你喜欢

热点阅读