`
uule
  • 浏览: 6384114 次
  • 性别: Icon_minigender_1
  • 来自: 一片神奇的土地
社区版块
存档分类
最新评论

如何用消息系统避免分布式事务+TCC?

 
阅读更多

如何用消息系统避免分布式事务?

使用消息队列需要注意的几个关键问题

阿里云收发事务消息

 

 

前阵子从支付宝转账1万块钱到余额宝,我就思考支付宝扣除1万之后,如果系统挂掉怎么办,这时余额宝账户并没有增加1万,数据就会出现不一致状况了。

 

上述场景在各个类型的系统中都能找到相似影子,比如在电商系统中,当有用户下单后,除了在订单表插入一条记录外,对应商品表的这个商品数量必须减1吧,怎么保证?!在搜索广告系统中,当用户点击某广告后,除了在点击事件表中增加一条记录外,还得去商家账户表中找到这个商家并扣除广告费吧,怎么保证?!等等,相信大家或多或多少都能碰到相似情景。

 

本质上问题可以抽象为:当一个表数据更新后,怎么保证另一个表的数据也必须要更新成功。

 

 

1 、本地事务

还是以支付宝转账余额宝为例,假设有

 

支付宝账户表:A(id,userId,amount)

余额宝账户表:B(id,userId,amount)

用户的userId=1;

 

从支付宝转账1万块钱到余额宝的动作分为两步:

 

1)支付宝表扣除1万:update A set amount=amount-10000 where userId=1;

2)余额宝表增加1万:update B set amount=amount+10000 where userId=1;

 

如何确保支付宝余额宝收支平衡呢?

 

有人说这个很简单嘛,可以用事务解决。

Begin transaction
         update A set amount=amount-10000 where userId=1;
         update B set amount=amount+10000 where userId=1;
End transaction
commit;

 

非常正确,如果你使用spring的话一个注解就能搞定上述事务功能。

@Transactional(rollbackFor=Exception.class)
    public void update() {
        updateATable(); //更新A表
        updateBTable(); //更新B表
    }	

 

如果系统规模较小,数据表都在一个数据库实例上,上述本地事务方式可以很好地运行,但是如果系统规模较大,比如支付宝账户表和余额宝账户表显然不会在同一个数据库实例上,他们往往分布在不同的物理节点上,这时本地事务已经失去用武之地。

 

既然本地事务失效,分布式事务自然就登上舞台。

 

 

2 、分布式事务—两阶段提交协议

两阶段提交协议(Two-phase Commit,2PC)经常被用来实现分布式事务。一般分为协调器C和若干事务执行者Si两种角色,这里的事务执行者就是具体的数据库,协调器可以和事务执行器在一台机器上。



1) 我们的应用程序(client)发起一个开始请求到TC(事物协调器);

 

2) TC先将<prepare>消息写到本地日志,之后向所有的Si发起<prepare>消息。以支付宝转账到余额宝为例,TC给A的prepare消息是通知支付宝数据库相应账目扣款1万,TC给B的prepare消息是通知余额宝数据库相应账目增加1w。为什么在执行任务前需要先写本地日志,主要是为了故障后恢复用,本地日志起到现实生活中凭证 的效果,如果没有本地日志(凭证),出问题容易死无对证;

 

3) Si收到<prepare>消息后,执行具体本机事务,但不会进行commit,如果成功返回<yes>,不成功返回<no>。同理,返回前都应把要返回的消息写到日志里,当作凭证。

 

4) TC收集所有执行器返回的消息,如果所有执行器都返回yes,那么给所有执行器发生送commit消息,执行器收到commit后执行本地事务的commit操作;如果有任一个执行器返回no,那么给所有执行器发送abort消息,执行器收到abort消息后执行事务abort操作

 

注:TC或Si把发送或接收到的消息先写到日志里,主要是为了故障后恢复用。如某一Si从故障中恢复后,先检查本机的日志,如果已收到<commit >,则提交,如果<abort >则回滚。如果是<yes>,则再向TC询问一下,确定下一步。如果什么都没有,则很可能在<prepare>阶段Si就崩溃了,因此需要回滚。

 

现如今实现基于两阶段提交的分布式事务也没那么困难了,如果使用java,那么可以使用开源软件atomikos(http://www.atomikos.com/)来快速实现。

 

不过但凡使用过的上述两阶段提交的同学都可以发现性能实在是太差,根本不适合高并发的系统。为什么?

 

1)两阶段提交涉及多次节点间的网络通信,通信时间太长!

2)事务时间相对于变长了,锁定的资源的时间也变长了,造成资源等待时间也增加好多!

正是由于分布式事务存在很严重的性能问题,大部分高并发服务都在避免使用,往往通过其他途径来解决数据一致性问题。

 

 

3 、使用消息队列来避免分布式事务

如果仔细观察生活的话,生活的很多场景已经给了我们提示。

 

比如在北京很有名的姚记炒肝点了炒肝并付了钱后,他们并不会直接把你点的炒肝给你,而是给你一张小票,然后让你拿着小票到出货区排队去取。为什么他们要将付钱和取货两个动作分开呢?原因很多,其中一个很重要的原因是为了使他们接待能力增强(并发量更高)。

 

还是回到我们的问题,只要这张小票在,你最终是能拿到炒肝的。同理转账服务也是如此,当支付宝账户扣除1万后,我们只要生成一个凭证(消息)即可,这个凭证(消息)上写着“让余额宝账户增加 1万”,只要这个凭证(消息)能可靠保存,我们最终是可以拿着这个凭证(消息)让余额宝账户增加1万的,即我们能依靠这个凭证(消息)完成最终一致性。

  

3.1 如何可靠保存凭证(消息)

 

有两种方法:

3.1.1 业务与消息耦合的方式

支付宝在完成扣款的同时,同时记录消息数据,这个消息数据与业务数据保存在同一数据库实例里(消息记录表表名为message)。

Begin transaction
         update A set amount=amount-10000 where userId=1;
         insert into message(userId, amount,status) values(1, 10000, 1);
End transaction
commit;

 上述事务能保证只要支付宝账户里被扣了钱,消息一定能保存下来。

 

当上述事务提交成功后,我们通过实时消息服务将此消息通知余额宝,余额宝处理成功后发送回复成功消息,支付宝收到回复后删除该条消息数据。

 

3.1.2 业务与消息解耦方式

上述保存消息的方式使得消息数据和业务数据紧耦合在一起,从架构上看不够优雅,而且容易诱发其他问题。为了解耦,可以采用以下方式。

 

1)支付宝扣款事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送;

 

2)当支付宝扣款事务被提交成功后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才真正发送该消息

 

3)当支付宝扣款事务提交失败回滚后,向实时消息服务取消发送。在得到取消发送指令后,该消息将不会被发送;

 

4)对于那些未确认的消息或者取消的消息,需要有一个消息状态确认系统定时去支付宝系统查询这个消息的状态并进行更新。为什么需要这一步骤,举个例子:假设在第2步支付宝扣款事务被成功提交后,系统挂了,此时消息状态并未被更新为“确认发送”,从而导致消息不能被发送。

 

优点:消息数据独立存储,降低业务系统与消息系统间的耦合

缺点:一次消息发送需要两次请求;业务处理服务需要实现消息状态回查接口。

 

3.2 如何解决消息重复投递的问题

还有一个很严重的问题就是消息重复投递,以我们支付宝转账到余额宝为例,如果相同的消息被重复投递两次,那么我们余额宝账户将会增加2万而不是1万了。

 

为什么相同的消息会被重复投递?比如余额宝处理完消息msg后,发送了处理成功的消息给支付宝,正常情况下支付宝应该要删除消息msg,但如果支付宝这时候悲剧的挂了,重启后一看消息msg还在,就会继续发送消息msg。

 

解决方法很简单,在余额宝这边增加消息应用状态表(message_apply),通俗来说就是个账本,用于记录消息的消费情况,每次来一个消息,在真正执行之前,先去消息应用状态表中查询一遍,如果找到说明是重复消息,丢弃即可,如果没找到才执行,同时插入到消息应用状态表(同一事务)。

 

for each msg in queue
  Begin transaction
    select count(*) as cnt from message_apply where msg_id=msg.msg_id;
    if cnt==0 then
      update B set amount=amount+10000 where userId=1;
      insert into message_apply(msg_id) values(msg.msg_id);
  End transaction
  commit;

 

4、消息处理模型

 

点对点


 

发布订阅


4.2 消息的可靠性

 

消息的发送端的可靠性:发送端处理完成后一定能将消息成功发送到消息系统

消息的接收端的可靠性:接收端仅且能够从消息中间件成功消费一次消息。

 

发送端的可靠性

 

在本地数据建一张消息表,将消息数据与业务数据保存在同一数据库实例里这样就可以利用本地数据库的事务机制事务提交成功后,将消息表中的消息转移到消息中间件,若转移消息成功则删除消息表中的数据,否则继续重传。

 

接收端的可靠性

 

保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。

保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号 

 

。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 62.8 KB
  • 大小: 5.8 KB
  • 大小: 17.3 KB
分享到:
评论

相关推荐

    44_了解分布式事务方案吗?你们都咋做的?有啥坑?.zip

    本资料包提供了多种分布式事务解决方案的深入探讨,包括单块系统中的事务、两阶段提交、本地消息表、TCC(Try-Confirm-Cancel)方案、可靠消息最终一致性以及最大努力通知方案。 首先,单块系统里的事务主要指传统...

    TCC实现分布式事物,java 源码

    分布式事务是现代软件系统中处理跨数据库或跨服务一致性的重要技术。TCC(Try-Confirm-Cancel)模式是一种实现分布式事务的策略,它允许在不同的应用程序和服务之间保持数据的一致性。本文将深入探讨TCC的基本原理、...

    rest-tcc分布式事务

    ### REST-TCC分布式事务 #### 一、引言与背景 在RESTful Web服务领域,原子性事务的支持一直是讨论的热点。尽管HTTP协议定义的GET、PUT、DELETE等请求具有幂等性,使得单个服务的客户端交互具备了一定程度的可靠性...

    浅谈分布式事务实现技术及应用场景探讨.pdf

    4. 异步确保(Asynchronous Ensure):是一种基于消息队列的分布式事务实现技术。它可以确保事务的执行顺序和可靠性,避免了分布式事务中的竞争Condition。 5. 最大努力通知(Best-Effort Notification):是一种...

    分布式事务-可靠消息的服务的设计与实现(消息服务子系统)

    本文将深入探讨“分布式事务-可靠消息的服务的设计与实现”这一主题,主要围绕消息服务子系统,结合提供的资料,包括“微服务架构的分布式事务解决方案.pdf”、“rc_pay_dubbo_message.sql”数据库脚本、“龙果学院-...

    Dubbo分布式事务解决方案

    4. **实施难度**: 大型企业通常会自主研发分布式事务框架或消息中间件,但对于中小型企业来说,这样的投入成本过高。 #### 四、分布式事务解决方案 为了解决上述挑战,本教程提出了一种轻量且灵活的分布式事务解决...

    分布式事务.pdf

    除此之外,也有基于消息队列、补偿事务(TCC)等不同实现方式的分布式事务处理策略。 然而,在实际应用中,分布式事务的引入会增加系统的复杂度和开销,因此在决定是否采用分布式事务时,应该充分评估业务需求和...

    【Java】分布式事务tcc补偿解决方案.zip

    Java环境下对于分布式事务TCC补偿解决方案的研究和应用,需要开发者具备分布式系统的基础知识,熟悉事务管理机制,并能够合理利用框架和工具来解决实际问题。随着微服务架构的进一步发展和应用,理解和掌握TCC模型将...

    分布式事务专题-v1.1.pdf

    消息驱动的事务机制通常与补偿事务(也称为 saga 模式)相结合,用以处理分布式事务。在 saga 模式中,一系列本地事务通过补偿操作来达到全局的事务一致性。 除了传统的分布式事务解决方案外,还有一些基于特定数据库...

    dubbo分布式tcc事务demo

    【标题】"dubbo分布式TCC事务demo"是关于如何在分布式系统中使用TCC(Try-Confirm-Cancel)事务模式的一个示例项目。TCC事务模式是解决分布式环境中数据一致性问题的一种方法,尤其适用于微服务架构。在这个demo中,...

    TCC分布式事务_hy4.zip

    TCC分布式事务是一种确保分布式系统中数据一致性的重要技术。在当今互联网技术中,随着微服务架构的普及,系统间的通信越来越多地采用远程过程调用(RPC)或消息传递机制,这导致了事务的边界不再局限于单一的数据源...

    分布式事务详细介绍

    分布式事务是指在分布式系统中,为了保持事务的ACID(原子性、一致性、隔离性、持久性)特性,需要跨越多个资源管理器(如数据库、消息队列等)进行协调的一系列操作。在分布式系统中,事务的操作分布在不同的节点上...

    【分布式事务】java分布式事务-服务端_pgj.zip

    在Java开发中,常用的分布式事务解决方案包括两阶段提交(2PC)、补偿事务(TCC)、本地消息表、最终一致性等模式。这些技术分别有不同的适用场景和优缺点,开发人员需要根据具体的业务需求和系统环境来选择合适的...

    TCC-分布式事务.zip

    在Try阶段,TCC只进行资源预留操作,并不真正执行业务逻辑,这样可以降低系统在进行分布式事务时的锁等待时间,减少资源争用。在Confirm阶段和Cancel阶段,TCC允许事务最终一致性,这意味着如果业务操作无法在有限...

    tcc分布式事务.zip

    TCC分布式事务模型为解决分布式系统中的一致性问题提供了一种可行的方案,但同时也要求系统设计者和开发者必须具备深入的理解和精心的设计,以及对于可能出现的问题有充分的准备。通过理解和应用TCC模型,开发者可以...

    tcc-transaction分布式事务-hy4.zip

    分布式事务是计算机科学中的一项重要概念,用于解决分布在多...通过学习和使用“tcc-transaction分布式事务_hy4.zip”这类资源,开发者可以更加深入地理解和掌握分布式事务的实现方法,并将其应用于实际的项目开发中。

    TCC分布式事务_hy5.zip

    TCC分布式事务模型通过将事务分解为Try、Confirm和Cancel三个阶段,为分布式系统提供了一种高效且灵活的事务管理方案。开发者在实现TCC模型时需要考虑业务逻辑的正确划分、状态管理和异常处理等多个方面,以确保在...

    tcc分布式事务_hy4.zip

    TCC分布式事务模型为开发者提供了一种处理复杂分布式系统事务问题的有效手段,但是需要结合实际业务情况,合理设计和调整,才能充分发挥其优势,避免潜在的风险。而本文件包zzy-tcc-transaction-master则是实现和...

Global site tag (gtag.js) - Google Analytics