美文网首页分布式架构
分布式事务 - 伪代码解读

分布式事务 - 伪代码解读

作者: 泽_3b5f | 来源:发表于2019-08-25 15:25 被阅读0次

随着数据规模不断上涨,数据操作的性能越来越低,为了提升性能,在数据库层面,通常通过分库分表提升性能,提升吞吐量,但也引发出一些新的问题,如分布式查询,分布式事务等。本文就分布式事务,对目前市面上主流的解决方案进行解读,让大家更好地了解、使用好分布式事务。

问题是如何出现的

分库分表前

同一个数据库,同一个数据库连接,依赖数据库实现事务功能。

db.connection{
    begin
        sql 1
        sql 2
        ...
    commit or rollback
}
// 事务实现了
db.connection()

分库分表后

不同数据库,不同数据库连接,如何实现事务功能?

db1.connection{
    begin
        sql 1
        sql 2
        ...
    commit or rollback
}
db2.connection{
    begin
        sql 1
        sql 2
        ...
    commit or rollback
}
// 这时1、2作为一个整体事务,没有实现最终一致
db1.connection() && db2.connection() 

解决方案

2PC(Two-Phase Commit)

概念:事务实际分为两部分

第一部分 prepare 预执行

begin
  sql 1
  sql 2
  ...

第二部分 commit or rollback 确认

commit

在网络正常,数据库正常的情况下,过了第一部分的数据操作,肯定能commit成功(原理可以了解数据库事务和锁)

rollback

第一部分出现问题(比如数据逻辑问题),取消事务的提交

解决方法伪代码

执行代码{
    //一阶段
    aStatus=参与者A.prepare()
    bStatus=参与者B.prepare()
    
    //二阶段
    if aStatus and bStatus:
        参与者A.commit()
        参与者B.commit()
    else:
        参与者A.rollback()
        参与者B.rollback()
}

存在问题

同步阻塞:执行过程中,所有参与者都是阻塞的。
单点故障: 代码(也叫协调者)在执行完之前发生故障,尤其是刚完成一阶段进入二阶段,那么所有参与者都在阻塞状态。
数据不一致:二阶段由于网络终端等原因,部分参与者执行commit,部分参与者没有执行commit,数据最终不一致
遗留问题:代码执行到 参与者A.commit() 后宕机,同时参与者A也宕机,这时候没人知道事务是否已经成功(发送成功,或者执行成功)

适用场景

3PC(Two-Phase Commit)

原理

减少因网络等异常造成的长时间阻塞

  1. preapre前确认各参与方是否可执行事务;
  2. 引入超时机制,最多等待N秒,然后直接commit或回滚。
执行代码{
    //一阶段
    参与者A.ping()
    参与者B.ping()
    
    //一阶段 --> 二阶段
    aStatus=参与者A.prepare().timeout(seconds).returnFalse()
    bStatus=参与者B.prepare().timeout(seconds).returnFalse()
    
    //二阶段 --> 三阶段
    if aStatus and bStatus:
        参与者A.commit().timeout(seconds).returnFalse()
        参与者B.commit().timeout(seconds).returnFalse()
    else:
        参与者A.rollback().timeout(seconds).returnFalse()
        参与者B.rollback().timeout(seconds).returnFalse()
}

改进地方

1.在prepare前,增加一个阶段,检查网络等的资源是否可用
2.prepare阶段,增加超时机制

是的,数据不一致的问题,依旧没有解决,只是缓解了阻塞和单点问题

TCC(try,commit,cancel)

原理

把事务逻辑从sql,提取出来,自行编码实现

执行代码{
    //try阶段,类似信用卡预授权,先冻结额度,未实际扣除
    aStatus=参与者A.冻结资源().commit()
    bStatus=参与者B.冻结资源().commit()
    
    //Confirm阶段,实际扣除(并解冻)
    if aStatus and bStatus:
        (参与者A.扣除资源().commit()).异步执行()
        (参与者B.扣除资源().commit()).异步执行()
    
    else:
        //Cancel阶段,取消冻结
        (参与者A.解冻资源().commit()).异步执行()
        (参与者B.解冻资源().commit()).异步执行()
}

改进地方

1.消除由于多个数据库事务造成长时间阻塞问题,大大提高了吞吐量
2.可以比较方便实现最终数据一致性

存在问题:所有参与者都需要实现 冻结、扣除、解冻 三个逻辑。且扣除和解冻者两个操作,需要实现幂等。代码实现成本比较高。

QA

  1. 为什么try阶段有commit?

单个参与者资源的冻结(可能有局部事务),不需要分布式事务(全局)

  1. 为什么confirm阶段要用thread? 而try阶段没有

理论上,只要通过try阶段(冻结),confirm阶段肯定能执行(除非数据库,网络,账号被封等异常),因此可并行执行。try阶段只能串行,因为要确认所有资源均足够执行这次分布式事务。

基于可靠消息(mq版)

原理

  1. 用mq消息异步传递子事务状态,最终达到全局事务的完成。
  2. 特点:由发起方决定是否回滚,也就是说只要发起者成功,后续的子事务基本都能成功。比如刷卡后,增加消费积分。
执行(事务主题, transId, 参与者){
    name = 参与者.名称()
    
    mq.setMsg(事务主题, transId, name, 'ping')
    mStatus = 参与者.prepare();
    mq.setMsg(事务主题, transId, name, 'prepare')
    if mStatus:
        excuted = 数据库是否提交(transId, name)
        if not excuted:
            参与者.commit()
        mq.setMsg(事务主题, transId, name, 'commit')
    else:
        参与者.rollback()
        mq.setMsg(事务主题, transId, name, 'rollback')
}

发起者A{
    transId = getTransactionId()
    执行(事务主题X, transId, 参与者A)
    name = 参与者A.名称()
    mq.订阅事件(事务主题X, name).触发函数(e){
        transId = e.transacationId;
        定时任务(事务主题X, transId, name)
        if e.message == 'commit':
            //启动下一个兄弟事务
            mq.setMsg(事务主题X, transId, 参与者B名称, "wake up and work")
        else if e.message == 'rollback':
            mq.delMsg(事务主题X, transId)
    }
}

参与者B{
    name = 参与者B.名称()
    mq.订阅事件(事务主题X, name).触发函数(e){
        transId = e.transacationId;
        定时任务(事务主题X, tranId,name)
        if e.message == 'wake up and work':
            执行(事务主题X, transId, 参与者B)
        else if e.message == 'commit':
            mq.delMsg(事务主题X, transId)
        else if e.message == 'rollback':
            //人工处理,因为一般都能执行成功
    }
}

QA

  1. 数据库操作发送mq信息,不能确保同时(不)发生,如何保证事务一致性?

为确保事务能正常工作,需对每个参与者的事务状态,进行超时检测,并作出处理(俗称捞起)。

比如:实际进行到了prepare阶段,但事务状态在ping阶段,需回补一条prepare消息。

比如:commit后,消息发送失败,消息状态仍为prepare,此时需确认事务是否已commit,来决定只发送commit消息,或者重新执行commit并发送commit消息。

最后为了达到幂等,还需要对每个数据库的commit,进行是否commit的确认。

数据库是否提交(transId,name){
    
}

定时任务{
    name, status  = mq.get(事务主题X, transId)
    if status == 'prepare':
        excuted = 数据库是否提交(transId,name)
        if excuted:
            //启动下一个兄弟事务
            mq.setMsg(事务主题, transId, name, 'commit')
        else:
            //执行
            参与者实例 = 实例化(name)
            参与者实例.commit()
            mq.setMsg(事务主题, transId, name, 'commit')
}

SAGA

原理

(消息)异步 + TCC(部分)

执行代码{
    总事务开始() 

    new Thread(
        aStatus = 参与者A.冻结资源().commit()
        if aStatus:
            参与者A.扣除资源().commit()
        else
            参与者A.解冻资源().commit()
    ).start()
    
    new Thread(
        bStatus = 参与者A.冻结资源().commit()
        if bStatus:
            参与者B.扣除资源().commit()
        else:
            参与者B.解冻资源().commit()
    ).start()
}

自行实现的定时任务{
    if 全部参与者已完成事务:
       if 全部扣除commit成功:
            总事务成功()
        else
            已扣除资源退还()
            总事务失败()
}

额外- 阿里Seata

原理

  1. 一个事务由多个子事务组成,每个子事务直接commit,不需要等待其它兄弟事务确认prepare或者commit
  2. 有一个兄弟事务rollback,则全部兄弟事务rollback。
  3. 框架实现 commit后rollback,不需要业务方编码!!实现机制可以到官方查看
执行代码{
    框架开始监听该事务()
    参与者A.扣除资源().commit()
    参与者B.扣除资源().commit()
}

框架自动实现的回滚操作{
    if 兄弟事务发生rollback:
        全部兄弟rollback()
}

QA

  1. 这个框架这么牛EN,其它方式还有存在的必要么?
1.业务场景不一定全部适用
2.需控制全部参与者的数据源,旧系统迁移成本很高

参考文档:
https://mp.weixin.qq.com/s/T-Q9eouj4unrWh8Q9bJoOA
https://www.cnblogs.com/jajian/p/10014145.html

相关文章

  • 分布式事务 - 伪代码解读

    随着数据规模不断上涨,数据操作的性能越来越低,为了提升性能,在数据库层面,通常通过分库分表提升性能,提升吞吐量,但...

  • 伪代码解读VirtualApk

    [TOC] 前言 “二八定律”在软件行业也有体现,甚至更为极端。据说90%的代码在平时都不会运行到。比如异常处理、...

  • CockroachDB事务代码解读

    Coordinator CockroachDB从执行层面起始于client.TXN 它包含四个比较关键的字段: T...

  • 微服务架构下分布式事务解决方案 —— 阿里GTS

    摘要: 本文将深入和大家探讨微服务架构下,分布式事务的各种解决方案,并重点为大家解读阿里巴巴提出的分布式事务解决方...

  • 微服务架构下分布式事务解决方案 —— 阿里GTS

    摘要:本文将深入和大家探讨微服务架构下,分布式事务的各种解决方案,并重点为大家解读阿里巴巴提出的分布式事务解决方案...

  • 分布式事务与分布式锁

    一、分布式事务 什么事分布式事务 分布式事务就是指事务的资源分别位于不同的分布式系统的不同节点之上的事务。 分布式...

  • 微服务分布式事务--破局

    微服务架构下分布式事务设计实战 商品 订单 支付 分布式事务->长事务本地事务->短事务 分布式事务: 比如 下...

  • 分布式事务

    目录 分布式事务解决方案 长事务: saga 短事务: 设计的时候尽量短事务,能不用分布式事务尽量不用,分布式事务...

  • kotlin 协程学习笔记(二)Retrofit的协程原理

    源码解读基于2.9.0本笔记记录本人阅读源码思路,并非源码解读不想看阅读源码思路的直接拉倒最下面看伪代码 该代码块...

  • ATOMIKOS+JTA分布式事务记录

    ATOMIKOS+JTA是用来分布式事务的中间件,那么什么是分布式事务呢? 事务,分为单机事务,分布式事务;单机事...

网友评论

    本文标题:分布式事务 - 伪代码解读

    本文链接:https://www.haomeiwen.com/subject/innvgqtx.html