分布式事务

157 阅读5分钟

分布式事务

分布式系统中,涉及到多个数据库和多个应用程序之间的事务处理。

两阶段提交 2PC

事务管理器分为两个阶段管理事务

  1. 准备。
  2. 资源提交或者回滚。

核心思想

2PC协议的核心思路是协调者和参与者通过两个阶段的协商达成最终操作的一致性。首先,第一阶段的目的是确认各个参与者是否具备执行事务的条件。根据第一阶段参与者的响应结果,制定出第二阶段的事务策略。如果第一阶段中任意一个参与者不具备事务执行条件,那么第二阶段的决策就是回滚事务。只有在所有参与者都具备事务执行条件的情况下,才进行整体事务的提交。

2PC存在的问题

  1. 同步阻塞:因为所有参与者都是事务阻塞型的。当参与者占用公共资源时,其他第三方节点访问就不得不处于阻塞状态。

  2. 单点故障:当协调者发生故障,参与者就会一直阻塞下去……

参与者挂掉

如果在第一阶段,协调者发送Prepare指令给所有的参与者后,参与者挂掉了,那么此时协调者因为迟迟收不到参与者的消息而导致超时,所以协调者在超时之后会统一发送abort指令进行事务回滚。

协调者挂掉

seata

Seata的AT模式

这是两阶段提交模式(2PC)

两阶段

  • 第一阶段就直接提交事务,记录提交日志

  • 第二阶段时,再由TM决定是否异步提交或者回滚。(回滚根据undo_log表

通过代理数据源的方式,使得本地事务与全局事务能够统一管理

异步提交失败了怎么办?

  • 利用回滚日志进行补偿:如果二阶段异步提交失败,Seata 会使用一阶段生成的回滚日志(undo_log)进行反向补偿。回滚日志记录了数据在业务操作前的状态以及相关的业务 SQL 信息,通过这些信息可以生成并执行回滚语句,将数据恢复到事务执行前的状态,以保证数据的一致性。
  • 事务状态管理与协调:事务协调器(TC)会对全局事务的状态进行管理和跟踪。当异步提交失败时,TC 会将全局事务的状态设置为相应的错误状态,并根据具体情况决定是否需要通知其他参与事务的分支进行回滚操作。各分支事务会根据 TC 的指令来执行相应的操作,确保整个分布式事务的一致性。
  • 重试机制:Seata 框架可能会自动触发重试机制,尝试重新执行异步提交操作。通过一定的重试策略,如固定间隔重试或指数退避重试等,来增加提交成功的机会。如果重试多次后仍然失败,才会采取更严格的措施,如回滚事务。

写隔离

  • 一阶段本地事务提交前,需要确保先拿到 全局锁

  • 拿不到 全局锁 ,不能提交本地事务。

  • 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

本地事务写数据后,需要获取全局锁才能提交本地事务,如果获取不到,会重试获取全局锁(最多三次,超过则回滚)。

所以不会发生脏写问题。

seata.apache.org/zh-cn/docs/…

本地锁的粒度:数据库的行锁

在一阶段中提交了,此时数据库中另外的事务将该数据修改了,那么在第二阶段还能回滚吗

需要根据配置的策略来做处理(回滚失败?)

一般来说会抛出异常,然后需要人工处理

针对于这种情况,还可以将这个表的字段拆分一下,将可能会进场修改的字段提取出来,减少主信息被修改的可能。

脏写

全局事务的一阶段中,某个分支事务执行完成提交后,其他非全局事务将这个分支事务修改的数据再修改了(脏写),就会导致原来的全局事务无法回滚。

如何防止脏写?

  1. 其他的事务也加上@GlobalTransactional注解,但是这样导致其他的非分布式事务也要像TCC注册分支事务

  2. @GlobalLock + select for update

@GlobalLock 注解

  • 在非全局事务方法中,强制获取Seata全局锁,从而防止与正在进行的全局事务发生脏读和脏写问题。

  • 如果没有获取到锁,则会抛出异常。

  • 这个注解不会开启全局事务。

  • 基于AOP实现的。

使用场景:

  • 在非事务方法中需要操作全局事务中的数据时
  • 需要获取最新已提交数据,避免读到未提交的中间状态

undo_log表log_status=1的记录是做什么用的?

主要用于防止资源悬挂

  • 插入防御性记录:当分支事务回滚时,如果发现回滚undo记录还未插入,就会插入一条log_status = 1undo记录。这条记录相当于一个 “占位符”,用于标识该分支事务可能存在资源悬挂的风险。
  • 利用唯一索引冲突阻止错误提交:undo表通常会设置唯一索引,以确保数据的一致性。当该分支事务的本地事务(包含业务写操作 SQL 和对应的undo操作在一个本地事务中)尝试提交时,由于log_status = 1的记录已经存在,会导致唯一索引冲突,从而使本地事务提交失败。这样就避免了分支事务在全局事务回滚后仍然提交,防止了资源悬挂的发生。

seata.apache.org/zh-cn/docs/…