深度解析Seata AT 模式中性能优化与隔离保障的平衡之道

本文深入探讨Seata AT模式在解决脏写和脏读问题上的策略,包括通过锁的设计减少资源锁定以提升性能,以及在1阶段和2阶段中如何保证写隔离和读隔离。Seata通过全局锁和本地锁的协作,避免了脏写和脏读,同时介绍了在特定场景下如何启用全局锁以确保写隔离。
摘要由CSDN通过智能技术生成

一、脏写和脏读

数据隔离性可从读和写两个维度来审视:

  • 从写的视角来看,是不能有脏写的,即不能让一个事务修改另外一个事务修改过但尚未提交的数据。

    • 对于成熟的数据库产品来说,脏写这种情况是不允许发生的。所以在多个未提交事务相继对一条记录做改动时,需要让它们排队执行,这个排队的过程其实是通过锁来实现的。
  • 从读的视角来看,通常数据库产品默认隔离级别为读已提交(Read committed),比如 OracleSQLServer;虽然 Mysql 的默认隔离级别为可重复读(Repeatable read),但通常来说隔离级别越高,性能损耗越大,而且读已提交能够满足业务绝大部分场景,所以有些公司的 MySQL 也采用了读已提交的隔离级别。

    • 读已提交的隔离级别解决的是脏读的问题,脏读就是一个事务读取到了另外一个事务修改后尚未提交的数据
    • 脏读会有什么问题呢?比如:A 事务读取 B 事务尚未提交的数据,此时如果 B 事务发生错误并执行回滚操作,那么很容易理解 A 事务获取、使用被回滚的数据肯定是会有问题的。

避免脏写和脏读是对数据库相关产品基本且必要的要求,所以 Seata 提供的分布式事务管理能力也要具备避免脏写和脏读的能力

二、锁的设计是引发脏读和脏写的关键

假定您已了解,数据库 XA 协议无论 Phase2 的决议是 commit 还是 rollback,事务性资源的锁都要保持到 Phase2 完成才释放,如下图所示:

而绝大部分情况下全局事务是成功提交的,那么绝大部分情况下,可以省去 Phase2 的资源锁定(数据库锁和数据库连接),如下图所示:

这个设计,给整体性能的提升提供了支撑,原因就在与他极大地减少了分支事务对资源(连接和锁)的锁定时间:

  1. 在分支事务 Phase1 结束时,本地 DB 连接也得以释放。
  2. 因分支事务中数据的本地 DB 锁由本地 DB 事务管理,在分支事务 Phase1 结束时释放,这时候其他本地 DB 事务就能读取到最新的数据。

提示:在绝大部分应用在读已提交的隔离级别下工作是没有问题的,本地事务的锁这么早释放,2 阶段没有本地事务锁的保障,是否会出现读未提交的问题呢?实际上,有不少应用场景在读未提交的隔离级别下是没有问题的,而锁在 1 阶段释放掉带来的性能提升也是基于。

相较于 XA 模式 2 个阶段的本地 DB 资源锁定,AT 模式的初衷是减少非必要的 2 阶段本地 DB 资源的锁定,这其中为了保障隔离性,锁在两个阶段肯定是都不能少的(下文补充详情),基于复杂度不会减少只会迁移的的理论模型来推测,AT 模式一定是采用了其他锁方案来替代 2 阶段本地锁,即由 Seata TC 侧提供的全局锁,那么 Seata 就需协调本地锁全局锁来保障隔离性,其协作机制如下::

  1. 一阶段本地事务提交前,需要确保先拿到全局锁 。
  2. 拿不到 全局锁 ,不能提交本地事务。
  3. 拿全局锁有尝试上限(有限次数的重试),超出限制将放弃,并回滚本地事务,释放本地锁。

markdown复制代码分支事务一_开始
|
V 获取 本地锁
|
V 获取 全局锁    分支事务二_开始
|               |
V 释放 本地锁     V 获取 本地锁
|               |
V 释放 全局锁     V 获取 全局锁
                |
                V 释放 本地锁
                |
                V 释放 全局锁

如上所示,一个分布式事务的锁获取流程是这样的

  1. 先获取到本地锁即可修改本地数据,但不能直接提交本地事务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值