`

利用消息系统,避免分布式事物

阅读更多

一、本地事务
以支付宝转账余额宝为例,假设有 •支付宝账户表: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来快速实现。不过但凡使用过的上述两阶段提交的同学都可以发现性能实在是太差,根本不适合高并发的系统。为什么? 两阶段提交涉及多次节点间的网络通信,通信时间太长!
“玩转”Java 系列
—————————————————————————————
3
事务时间相对于变长了,锁定的资源的时间也变长了,造成资源等待时间也增加好多! 正是由于分布式事务存在很严重的性能问题,大部分高并发服务都在避免使用,往往通过其他途径来解决数据一致性问题。
三、使用消息队列来避免分布式事务
如果仔细观察生活的话,生活的很多场景已经给了我们提示。比如在北京很有名的姚记炒肝点了炒肝并付了钱后,他们并不会直接把你点的炒肝给你,而是给你一张小票,然后让你拿着小票到出货区排队去取。为什么他们要将付钱和取货两个动作分开呢?原因很多,其中一个很重要的原因是为了使他们接待能力增强(并发量更高)。还是回到我们的问题,只要这张小票在,你最终是能拿到炒肝的。同理转账服务也是如此,当支付宝账户扣除1万后,我们只要生成一个凭证(消息)即可,这个凭证(消息)上写着“让余额宝账户增加 1万”,只要这个凭证(消息)能可靠保存,我们最终是可以拿着这个凭证(消息)让余额宝账户增加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;
上述事务能保证只要支付宝账户里被扣了钱,消息一定能保存下来。当上述事务提交成功后,我们通过实时消息服务将此消息通知余额宝,余额宝处理成功后发送回复成功消息,支付宝收到回复后删除该条消息数据。
②业务与消息解耦方式
上述保存消息的方式使得消息数据和业务数据紧耦合在一起,从架构上看不够优雅,而且容易诱发其他问题。为了解耦,可以采用以下方式。1)支付宝在扣款事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送,只有消息发送成功后才会提交事务;2)当支付宝扣款事务被提交成功后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才真正发送该消息;3)当支付宝扣款事务提交失败回滚后,向实时消息服务

—————————————————————————————
4
取消发送。在得到取消发送指令后,该消息将不会被发送;4)对于那些未确认的消息或者取消的消息,需要有一个消息状态确认系统定时去支付宝系统查询这个消息的状态并进行更新。为什么需要这一步骤,举个例子:假设在第2步支付宝扣款事务被成功提交后,系统挂了,此时消息状态并未被更新为“确认发送”,从而导致消息不能被发送。优点:消息数据独立存储,降低业务系统与消息系统间的耦合;缺点:一次消息发送需要两次请求;业务处理服务需要实现消息状态回查接口。
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;

 

分享到:
评论

相关推荐

    maven springboot jta mybatis 分布式事物

    在本项目中,"maven springboot jta mybatis 分布式事物" 是一个核心主题,这涉及到几个关键技术和概念,下面将详细解释这些知识点。 首先,`Maven` 是一个构建工具,它帮助开发者管理项目的依赖关系,构建流程以及...

    C#数据库分布式事物使用

    当TransactionScope内的操作涉及到不同的数据库服务器时,.NET会自动提升为分布式事务,并利用分布式事务协调器(Distributed Transaction Coordinator,简称DTC)来保证跨系统的事务一致性。 5. **配置DTC** ...

    多数据源分布式事务管理调研报告.zip

    分布式事务是指跨越多个数据源(如数据库、消息队列或微服务)的单一逻辑操作,其目标是确保这些跨不同系统的操作要么全部成功,要么全部回滚,以保持数据的一致性。在Java领域,实现这种事务管理的挑战主要包括:...

    spring事物和rabbitMQ的例子

    在IT行业中,Spring框架是...通过这样的配置,你可以构建出健壮、可扩展的应用,能够处理复杂的业务逻辑,并有效地利用异步通信提升系统性能。在实际项目中,理解并熟练运用这些技术对于提升系统质量和稳定性至关重要。

    2021年信息系统分析与设计详细完整自考信息系统分析与设计复习资料整理.pdf

    分布式系统利用网络连接不同地点的资源,常见的模式有主从式和客户服务器模式。决策支持系统(DSS)则是为了解决半结构化决策问题,特点是交互式、模型驱动和支持概念。 信息化是指通过信息技术的广泛应用,推动...

    lcn的tx-manager

    集群间的通信和状态同步也是tx-manager设计中的关键部分,它通常利用诸如ZooKeeper、etcd等分布式协调服务来实现。 与Spring Cloud的集成是Lcn tx-manager的一大特点。Spring Cloud是微服务架构的常用框架,提供了...

    跨多个数据库操作,同时连接两个一上的数据库,用事物关联起来

    在IT行业中,尤其是在分布式系统和企业级应用中,经常需要处理跨多个数据库的操作。这种情况下,数据一致性变得至关重要,这就引入了"跨多个数据库操作"的概念。为了保证数据的一致性,我们需要一种机制来协调这些...

    LCN5.0.2同一个微服务模块多实例,TM会发生事务通知TC错乱的问题

    2. 使用连接池:为了提高效率和资源利用率,可以引入连接池管理TM与TC之间的连接,避免频繁创建和销毁连接。 3. 监控和报警:设置监控系统,当检测到连接异常或过高负载时及时发出警告,便于快速定位和解决问题。 4....

    2级access的复习用资料

    - 分布式数据库系统阶段:节点间数据分布式存储,逻辑上统一,物理上分散,提高了系统的可用性和扩展性。 - 面向对象数据库系统阶段:结合OOP思想,更灵活地表示现实世界实体,增强软件的重用性、灵活性和扩展性。...

    spring4.0.2+mybatis3.2.4+sharding-jdbc1.4.2实现分库分表、主从及事物支持

    总的来说,这个项目展示了如何通过Spring、MyBatis和Sharding-JDBC来构建一个高可用、高性能的分布式数据库系统,它能处理大规模数据并实现高效的事务处理,是企业级应用的良好实践。对于开发人员来说,理解和掌握...

    [精选]电算化会计-会计信息的组织.pptx

    死锁管理则避免系统因资源竞争陷入无法继续执行的状态。 数据文件的层次结构包括属性、字段、记录、文件和关键字。这些概念构成了数据操作的基础,如创建、查询、更新和删除等。同时,数据库视图、连接、存储过程、...

    无服务器构建双主架构最佳实践.pptx

    例如,利用消息队列(如Amazon SQS)和发布/订阅模式,即使单个实例出现故障,也能保证任务的异步处理和最终一致性。 构建全球双主架构时,我们需要考虑成本和复杂性之间的平衡。更高的可用性往往意味着更高的成本...

    zooKeeperApp

    4. 分布式事物:ZooKeeperApp支持原子性的读写操作,确保事务的正确性。 三、ZooKeeperApp与JavaScript结合 虽然ZooKeeper是用Java实现的,但JavaScript开发者可以通过ZookeeperClient等库来与ZooKeeper进行通信。...

    数据库系统-招标参数---模板.doc

    " " " "提供跨文件系统的数据管理,利用Filestream技术" " " "把文件管理统一到数据库管理中,避免了以前只是" " " "通过大二进制存储的方式存储,低效的访问方式。" " "数据库性能 "数据库提供数据表分区能力的...

    最新电子政务形成性考核册答案.doc

    概念构造是对现实世界的抽象,简化了事物的本质特征,而拓扑结构则表示系统中各元素的物理布局。 电子政务系统规划是将政府的战略目标转化为系统的具体目标、策略和技术实现。公文是政府公务信息的主要载体,视频...

    mybati事物配置

    MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。在 MyBatis 中,事务管理是确保数据一致性、完整性的关键部分。 在 MyBatis ...

    SqlServer数据库优化方案.pdf

    - 查询优化:分析和改进查询语句,避免全表扫描,利用索引,减少JOIN操作,使用存储过程和参数化查询。 - 索引管理:合理创建和维护索引,包括主键索引、唯一索引、非聚簇索引等,避免索引过多或过少。 - 表设计...

    hibernate缓存和事务

    Hibernate 是一个流行的对象关系映射(ORM)框架,它允许Java...通过理解Hibernate缓存和事务管理,以及如何有效地执行查询,开发者可以创建高效、健壮的Java应用程序,降低与数据库交互的复杂性,同时提升系统性能。

    数据库技术及应用知识点总结.doc

    - **字段命名规则**:避免空格开头,不使用特殊字符,长度不超过64个字符。 - **字段类型**:文本、数字、日期/时间、布尔(是/否)、查阅(下拉列表)。 - **字段属性**:字段大小、输入掩码、有效性规则、有效...

    实验报告4-资料.rar

    4. **事务管理**:在分布式系统中,事务管理是确保数据一致性的重要手段。Spring框架提供了强大的事务管理功能,支持编程式和声明式两种方式。声明式事务管理通过配置元数据(XML或注解)来控制事务边界,而编程式...

Global site tag (gtag.js) - Google Analytics