Seata之AT初识

292 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

简介

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

image.png

  • TC(Transaction Coordinator):事务协调器
  • TM(Transaction Management):事务管理器
  • RM(Resource Management):资源管理器

AT(Automatic(Branch) Transaction)

AT本质上是二阶段提交,Seata通过回滚日志记录记录提交前后的镜像实现回滚。

前提

  • 基于支持本地ACID事务的关系型数据库
  • Java应用,通过JDBC访问数据库。

和实现有关

二阶段

最终一致

  • 一阶段:业务数据和回滚日志记录在同一本地事务中提交,释放本地锁和连接资源。
    • 立即释放资源,不占用资源
  • 二阶段:如果TC收到了全部参与者的“提交成功”,那么就向所有参与者发起“正式提交”,收到“正式提交”成功后马上响应并释放全局锁,把需要后续处理的任务(批量地删除相应UNDO LOG记录)放入队列,后续异步进行批量处理;如果TC“收到”任意一个参与者“提交失败”,那么TC就发起回滚,利用第一阶段的日志回滚到之前的“状态”。

如何避免脏写?

因为是分两个阶段,第一个阶段就会释放资源,所以分布式事务还没完成之前可能会存在释放的记录被其他事务在原有基础上继续写,这样如果之前事务回滚了会导致脏写,因此在写之前需要进行控制发现之前的分布式事务还没结束就不允许“写成功”,seata通过全局锁来进行控制。

  • 一阶段本地事务提交前,需要确保先拿到 全局锁
  • 拿不到全局锁,就不能提交本地事务。
  • 全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

image.png 如图等tx1分布式事务提交之后释放了全局锁之后,tx2如果获取全局锁还没超时的话就获取成功提交本地事务。

image.png 如果tx1分布式事务失败了但是tx2持有了本地锁,需要等tx2拿锁超时之后回滚本地事务后,tx1再回滚,这样就回滚到了tx1提交之前的版本。

怎么避免脏读?

还是因为分两个阶段第一个阶段就释放资源的原因会导致脏读,seata还是通过全局锁实现,但是出于性能考虑没有对所有SELECT语句都进行代理,仅对FOR UPDATE的SELECT语句进行了代理获取全局锁。

image.png 如图中所示tx1先获取了全局锁,tx2需要等待,在这个过程中会进行释放本地锁和重试,查询是被block住的,等tx1释放了全局锁之后才能读成功,这样就避免了脏读。

可以看的出来,tx2整个过程一直在自旋,如果多个事务都和tx2一样谁获得并不一定,感觉这点有待优化。

优缺点

优点

  • 不占用资源,第一阶段就会释放。
  • 异步、批量处理分布式事务结束后的后续任务。

缺点

  • 基于支持本地ACID事务的关系型数据库
  • 第一阶段就释放资源,如果不加for update会导致脏读和数据不一致。
  • 脏读处理的不够好,性能不高,顺序不可控

可以和mysql MVCC一样只读的已提交的最新版本就行。

  • 最终一致性

来源

Seata AT 模式