分布式事务

1 2PC

  • 两阶段提交协议。它引入了一个事务协调者角色,来管理各个参与者
    • 请求提交阶段
      • 协调器向所有参与者发送事务请求,询问是否可以执行事务,然后各个参与者响应Yes或者No
    • 提交阶段
      • 基于第一阶段的投票结果进行决策
        • 当所有参与者同意提交,协调者才会通知各个参与者提交事务。
        • 否则协调者通知各个参与者取消事务。
      • 参与者接收到协调者发来的消息执行 本地commit或者 Rollback.
  • 优点
    • 利用数据库自身的功能进行本地事务的提交和回滚,也就是提交和回滚实际不需要我们实现。
      • 如mysql XA规范实现。
  • 不足
    • 提交协议是阻塞协议,如果事务协调器宕机,某些参与者将无法解决他们的事务问题
      • 同步阻塞
        • 基于数据库实现会锁住资源。
        • 不基于数据库实现,也可能要持数据库资源。
      • 单点故障
        • 事务协调者挂了,整个事务就执行不下去了。
          • 如 参与者发生完准备命令之后挂了,每个本地资源都会处于锁定状态。
      • 数据不一致问题
        • 网络抖动,导致某些参与者无法收到协调者的请求,而某些收到了,导致数据不一致。
  • XA 规范
    • 重做日志(redo log)
      • 每当有操作执行前,在数据真正更改前,会先把相关操作写入 redo 日志
    • 回滚日志(undo log)
      • 记录事务开始前数据的状态
    • 二进制日志(binlog)
      • 记录了所有的 DDL 和 DML 语句,除了数据查询语句 select、show 等,还包含语句所执行的消耗时间

2 3PC

  • 过程
    • 提交请求阶段
      • 协调器向所有参与者发送事务请求,询问是否可以CanCommit,然后各个参与者响应Yes或者No
    • 预提交
      • 协调者从所有参与者反馈都是Yes响应
        • 发送预提交请求
          • 向参与者发送预提交请求
        • 事务预提交
          • 参与者接收请求,会执行事务操作。
        • 响应反馈
          • 参与者成功执行了事务操作,返回Ack响应
      • 协调者从所有参与者反有一个No反应,或者超时
        • 发送中断请求
          • 协调者向所有参与者发送中断请求
        • 中断事务
          • 参与者收到中断请求,执行事务中断。
    • 提交( 只要预提交成功, 则一定要保证 真实提交成功,即使协调器下一阶段不可用,一般是通过重试补偿的策略)两种情况
      • 执行提交
        • 发送提交请求
          • 所有参与者返回Ack响应后,进入提交阶段,协调者向所有参与者发送提交请求。
          • 超时提交 如果参与者 超时没有收到提交请求,也进行提交。
        • 事务提交
          • 参与者接受到提交请求后,执行真正事务提交,完成事务释放资源。
        • 响应反馈
          • 事务提交后,参与者向协调者发送Ack响应。
        • 完成事务
          • 协调者接受到所有参与者Ack响应完成事务。
      • 中断事务
        • 协调者 预提交 没有收到参与者Ack 响应(非ack或者超市),执行中断。
  • 如何解决2pc同步阻塞问题
    • 协调者和参与者都引入了超时机制
  • 对2pc 改进
    • 引入超时机制
    • 添加预阶段,保证最后提交阶段参与者节点状态的一致性。

3 TCC

  • TCC 就是一种业务层面或者是应用层的两阶段提交
  • TCC 分为指代 Try、Confirm、Cancel 分为两个阶段
    • 第一阶段 try 完成业务检查(一致性)、预留业务资源(准隔离性)。
    • 第二阶段
      • Confirm 不做任何业务检查,仅仅使用预留的资源执行业务操作,如果失败会一直重试
      • Cancel 取消执行业务操作,释放预留的资源,如果失败会一直重试
  • 一个事务的所有服务都需要提供这三个方法 可以根据表字段去设计
//尝试方法
function try(){
    //记录日志
    todo save A 转出了 100 元 
    todo save B 转入了 100 元 
    //执行转账
    update amount set balacne = balacne-100 where id = 1
    update amount set balacne = balacne+100 where id = 2
}
//确认方法
function confirm(){
    //清理日志
    clean save A 转出了 100 元 
    clean save B 转出了 100 元 
}

//取消方法
function cancle(){
    //加载日志
    load log A
    load log B

     //退钱
    update amount set balacne = balacne+100 where id = 1
    update amount set balacne = balacne-100 where id = 2    
}

  • 场景
    • 分布式事务要求高,跟钱有关。
    • 每天系统执行时间比较短。
  • 特点
    • 对代码的嵌入性高,要求每个业务需要写三种步骤的操作
    • 对有无本地事务控制都可以支持使用面广
    • 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高
  • 注意点
    • 幂等问题,
      • 网络超时,会重复调用参与方的 confirm/cancel 方法
      • 异常事务的补偿执行 try 的重复执行。
    • 空回滚问题
      • try方法由于网络问题超时一直没有返回,事务管理器会发出Cancel命令 执行了cancel方法。
      • 解决:事务表 try方法成功执行后,会插入一条记录,标记分支事务 init 状态 表示 try执行成功。
        • cancel时 查询状态。
    • 悬挂问题
      • try方法由于网络问题超时,导致cancel先执行,然后再执行try方法。
      • 解决:空回滚 插入一条条记录,标记分支事务 rollbacked 状态。
        • try 开始执行的时候 首先尝试插入状态为 init 的分支事务
        • 如果失败表示当前分支事务的记录已经存在,try无需执行。

4 lcn 选取

  • 支持 TCC,TXC,LCN 三种模式
  • 协调控制流程
    • image.png
    • 三个角色, 发起方,参与方,txManager
      • 发起方 调用 txManager 创建事务组
      • 发起方 假如执行自己业务, 再调用参与方A,参与方A 执行自己事务,加入到事务组
        • 参与方A 出现异常, 不需要加入事务组,发起方捕获异常,通知事务回滚。
      • 发起方 知道 是提交事务还是回滚事务。 通知 txManager
      • txManager 通知每个事务参与者,并且每个参与者都响应给了txManager
      • txManager 响应给 发起方。

4.1 TXC 逆向sql

  • 第一阶段 执行sql的时候, 拦截并解析,查询出要受影响的数据。记录下来

  • 第二阶段

    • 提交 将记录的数据删除
    • 回滚 通过 记录的sql和影响的数据 创建逆向sql 并执行。
  • 流程
    image.png
  • 特点

    • 代码的嵌入性低
    • 限于对支持SQL方式的模块支持
    • 不会占用数据库的连接资源,但中间状态可见

4.2 Lcn模式

  • 流程
    image.png
  • 特点

    • 对代码的嵌入性为低。
    • 仅限于本地存在连接对象且可通过连接对象控制事务的模块
    • 事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障
    • 缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间, 会锁住资源。
  • 负载问题

    • 如 A 服务分别掉B服务集群 one tow 方法。 当负载到不同的 实例 导致出现两个事务, 下面代码 行锁 锁住资源导致 资源占用 导致窒息失败而回滚事务。
void one(id){
   execute => update demo set state = 1 where id = {id} ;
}

void two(id){
   execute => update demo set state = 2 where id = {id} ;
}

4.3 lcn 对一些问题的解决。

1 超时机制
  • 参与者 执行完业务,启动定时任务,到了时间 txManager 没有通知参与者
    • 主动请求 txManager
      • 成功 按响应结果 来回滚或者提交。
      • 失败 事务补偿。
2 补偿出现
  • 自动补偿

  • 手动补偿

  • 场景

    • 服务挂了
      • 执行完事务挂了
      • 没有执行事务挂了
      • lcn 记录开始执行业务之前 事务信息。
  • 记录发起方信息。

5 基于消息补偿的最终一致性

  • 基于消息队列的最终一致性就是一种异步事务机制,在业务中广泛应用
  • 基于消息补偿的一致性主要有本地消息表和第三方可靠消息队列等
    • 本地消息表
      • 将分布式事务拆分成本地事务进行处理,通过消息日志的方式来异步执行
      • 本地消息表是一种业务耦合的设计
        • 消息生产方需要额外建一个事务消息表,并记录消息发送状态
        • 消息消费方需要处理这个消息,并完成自己的业务逻辑
        • 有一个异步机制来定期扫描未完成的消息,确保最终一致性
        • 两个系统通知 可以用zookeeper, 或者接口。
  • image.png
    • 如 下单服务与库存服务
      • 系统收到下单请求,订单业务存储订单库,并同时存储该订单对应的消息数据,放入同一个事务处理
      • 库存服务 通过消息中间件收到库存更新消息,调整库存业务,同时返回业务处理结果。
      • 订单服务收到处理结果后,将本地消息表的数据删除或者设置为完成。
      • 设置异步任务,定时扫描本地消息表,发现有未完成的任务重试,保证最终一致性。
  • 缺点
    • 依赖 数据库消息表。所以并发不能太大。

6 可靠消息最终一致性。

  • image.png

7 最大努力通知方案

  • 系统 A 本地事务执行完之后,发送个消息到 MQ;
  • 这里会有个专门消费 MQ 的最大努力通知服务,这个服务会消费 MQ 然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统 B 的接口;
  • 是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。

8 你们公司是如何处理分布式事务的

  • 如何是特别严格,如资金 那我 tcc 来保证强一致性。

  • 可以不采用分布式事务, 人工去排除。

  • 两天,我把分布式事务搞完了

  • 分布式事务从0到1-了解TX-LCN原理

  • 终于有人把“TCC分布式事务”实现原理讲明白了!

你可能感兴趣的:(分布式事务)