什么是CAP定理?

  • Consistency 一致性:所有节点在同一时间看到的数据必须一致。
  • Availability 可用性:每个请求都能得到响应,不会出现长时间阻塞或错误。
  • Partition tolerance 分区容错性:当网络通信出现故障时(如网络丢包、节点之间无法连通),系统仍能继续运作。

在分布式系统中,网络分区无法避免,所以 P 必须保证。那么我们主要在AP/CP模式中做取舍。

CP:保证一致性 + 分区容错,牺牲可用性,适用于需要保证强一致允许响应慢或者直接拒绝请求

AP:保证可用性 + 分区容错,牺牲一致性,保证高可用,响应快但是允许短时数据不一致(我们的项目使用AP)

什么是BASE理论?

  • BA(Basically Available-基本可用):系统在出现故障或网络分区时,允许损失部分可用性,但必须保证核心功能可用

  • S(Soft state-软状态):一定时间内,系统中的数据可以是中间状态、未同步状态,允许临时不一致状态

  • E(Eventually consistent-最终一致性):虽然系统允许短时间不一致,但在乱状态结束后,最终达到数据一致

在实际项目中我们也是遵循BASE理论,基本采用的AP模式保证系统高可用,允许短时的数据不一致。

Seata中有哪些重要角色?

  1. TC(Transaction Coordinator)事务协调者:负责整个全局和分支事务事务的状态,协调全局事务的提交和回滚
  2. TM(Transaction Manager)事务管理者:定义全局事务范围,开始全局事务、提交/回滚全局事务
  3. RM(Resource Manager)资源管理者:管理分支事务处理的资源,与TC交谈,以注册分支事务和报告分支事务的状态,并驱动分支事务的提交/回滚。

image-20251117153956151.png

Seata有几种模式?

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入

  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式

  • TCC模式:最终一致的分阶段事务模式,有业务侵入

  • SAGA模式:长事务模式,有业务侵入

Nacos服务名称包括哪几部分?

namespace(命名空间)+group(组名称)+serviceName(服务名称)+cluster(集群名称)

Seata客户端获取TC的cluster名称的方式?

以tx-group-service的值为key到vgroupMapping中查找

image-20251117173721874.png

什么是2PC、3PC?

  • 2PC:两阶段提交
    1. 准备阶段:执行SQL语句但是不提交
    2. 提交/回滚阶段:等到所有节点都返回YES就commit,否则rollback
      • 优点:保证强一致性
      • 缺点:
        1. 同步阻塞,性能差
        2. 单点故障会一直等待
        3. 发出commit命令时可能有参与者没收到导致数据不一致
  • 3PC:三阶段提交
    1. 询问提交阶段:询问是否可以提交
    2. 预提交阶段:如果参与者都回复YES,则进入预提交阶段,锁定资源
    3. 提交阶段:通知参与者提交,如果超时由参与者自己决定提交或回滚
      • 改进点:超时参与者可以自己决定提交/回滚,避免单点故障等待问题
      • 仍存在发出commit命令时可能有参与者没收到导致数据不一致的问题

Seata的XA模式是怎么样的?

XA模式基于2PC协议,具有强一致性,但是性能差,依赖数据库支持,适用于银行金融等需要保证强一致的场景

image-20251117174205912.png

Seata的AT模式是怎么样的?

AT模式第一阶段直接提交,并记录数据快照写入undolog,如果参与者都成功,则提交事务,异步删除undolog;如果有失败,则通过数据快照回复原始数据,性能更好,保证了最终一致性,但是存在脏写的隔离性问题,项目中常用AT模式

image-20251117182538912.png

AT模式存在什么问题?如何解决的?

AT模式存在的最大问题就是脏写的问题,一阶段直接提交后数据可能被其他事务修改,而二阶段回滚后可能造成数据不一致;

Seata通过全局锁解决了脏写的问题,Seata 自己维护了一个逻辑全局锁,Seata 会向 TC 申请全局锁,如果全局锁冲突则必须等待/失败重试。

加了全局锁性能不就低了,那和XA模式有区别吗?

有区别!在锁粒度上有区别,AT性能更高。

XA模式的锁是数据库的锁,锁定了整行的数据;

而AT模式是Seata自己维护的逻辑锁,只锁定了字段,修改其他字段不影响。

如果其他事务不受Seata管理怎么办呢?

其实Seata不止记录了更新前的数据快照,也记录更新后的数据,回滚时采用一种类似CAS的方式比较数据有没有被修改,如果被修改了就发出异常,这时候就可以人工介入解决。

如果Undo Log写失败了怎么办?

Undo log 写入和业务 SQL 在同一个本地事务中执行,只要业务 SQL 成功,Undo log 一定存在且完整。

Seata的TCC模式是怎么样的?

  1. Try(预留资源):资源的检测和预留,如锁定库存、冻结余额

  2. Confirm(确认执行):完成资源操作业务;要求Try成功Confirm一定要能成功

  3. Cancel(取消回滚):预留资源释放,可以理解为Try的反向操作

  • 优点:
    1. 性能好:一阶段直接提交,相较于AT也无需生成快照和全局锁,性能最好
    2. 不依赖数据库事务,依赖手动编码的补偿,可用于不支持事务的数据库
  • 缺点:
    1. 有代码侵入,需要编写三份逻辑
    2. 无法保证强一致,只能保证最终一致性,存在软状态
    3. 需要考虑Confirm和Cancel的失败情况,需要做好幂等处理

image-20251117192030518.png

你是如何在代码中实现TCC的?

  1. 首先我们要接口定义三个方法代表 Try/Confirm/Cancel 三步;
  2. 然后加上@LocalTCC和@TwoPhaseBusinessAction注解;
  3. @TwoPhaseBusinessAction注解传入commit / rollback两个方法名,Try 自己就是被注解的方法;
  4. 在Service中实现这个接口;
  5. 然后在业务接口加上@GlobalTransactional,就实现了TCC。
1
2
3
4
5
6
7
8
9
10
11
12
13
@LocalTCC
public interface AccountTccAction {

@TwoPhaseBusinessAction(name = "accountTccAction",
commitMethod = "commit",
rollbackMethod = "rollback")
boolean prepare(@BusinessActionContextParameter(paramName = "userId") Long userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount);

boolean commit(BusinessActionContext context);

boolean rollback(BusinessActionContext context);
}

TCC存在什么问题?如何解决?

问题:

  1. 幂等性:Confirm和Cancel失败重试,保证不重复执行
  2. 空回滚:当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。
  3. 业务悬挂:对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。

解决方案:

  1. 幂等回滚状态判断:在在执行操作前需要判断状态是不是已经是当前操作过的状态了,如果已经是说明已经处理过一次了,就直接返回。
  2. 空回滚冻结记录判断:通常在代码中,我们会在回滚前通过xid查询判断有没有冻结记录,有才执行回滚;如果没有,说明try还没做,我们就会新增一条空回滚的冻结记录,状态为回滚,但不真正回滚。
  3. 悬挂冻结记录判断:判断是否已经有过冻结记录,如果为空才执行try;如果不为空,说明Cancel执行过了,直接return拒绝。

Seata的Saga模式是怎么样的?

  1. 一阶段:直接提交本地事务

  2. 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚

  • 优点:

    1. 事务参与者可以基于事件驱动实现异步调用,吞吐高

    2. 一阶段直接提交事务,无锁,性能好

    3. 不用编写TCC中的三个阶段,实现简单

  • 缺点:

    1. 软状态持续时间不确定,时效性差

    2. 没有锁,没有事务隔离,会有脏写