`
jieke_ZJ
  • 浏览: 44875 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

浅谈分布式事务

 
阅读更多

前言应用场景

事务必须满足传统事务的特性,即原子性,一致性,分离性和持久性。但是分布式事务处理过程中,

某些场地比如在电商系统中,当有用户下单后,除了在订单表插入一条记录外,对应商品表的这个商品数量必须减1吧,怎么保证?

在搜索广告系统中,当用户点击某广告后,除了在点击事件表中增加一条记录外,
还得去商家账户表中找到这个商家并扣除广告费吧,怎么保证?

一 本地事务
以用户A转账用户B为例,假设有

  用户A账户表:A(id,userId,amount)  

  用户B账户表:B(id,userId,amount)

  用户的userId=1;

从用户A转账1万块钱到用户B的动作分为两步:

  1)用户A表扣除1万:update A set amount=amount-10000 where userId=1;

  2)用户B表增加1万:update B set amount=amount+10000 where userId=1;

  如何确保用户A用户B收支平衡呢?有人说这个很简单嘛,可以用事务解决。

1
2
3
4
5
<span style="color: #000000;">Begin transaction
update A set amount</span>=amount-10000 where userId=1<span style="color: #000000;">;
update B set amount</span>=amount+10000 where userId=1<span style="color: #000000;">;
End transaction
commit;</span>

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

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

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

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

XA是由X/Open组织提出的分布式事务的规范。XA规范主要 定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。
XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。
XA之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲(参考Fischer等的论文),两台机器理论上无 法达到一致的状态,需要引入一个单点进行协调。
事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或 JMS队列)。
下图说明了事务管理器、资源管理器,与应用程序之间的关系:

三 两阶段提交协议

分布式事务必须满足传统事务的特性,即原子性,一致性,分离性和持久性。但是分布式事务处理过程中,某些场地(Server)可能发生故障,
或 者由于网络发生故障而无法访问到某些场地。为了防止分布式系统部分失败时产生数据的不一致性。
在分布式事务的控制中采用了两阶段提交协议(Two- Phase Commit Protocol)。即事务的提交分为两个阶段:

  预提交阶段(Pre-Commit Phase)
  决策后阶段(Post-Decision Phase)

  两阶段提交用来协调参与一个更新中的多个服务器的活动,以防止分布式系统部分失败时产生数据的不一致性。例如,如果一个更新操作要求位于三个不同结点上的记录被改变,且其中只要有一个结点失败,另外两个结点必须检测到这个失败并取消它们所做的改变。

  为了支持两阶段提交,一个分布式更新事务中涉及到的服务器必须能够相互通信。一般来说一个服务器会被指定为"控制"或"提交"服务器并监控来自其它服务器的信息。

   在分布式更新期间,各服务器首先标志它们已经完成(但未提交)指定给它们的分布式事务的那一部分,并准备提交(以使它们的更新部分成为永久性的)。这是 两阶段提交的第一阶段。如果有一结点不能响应,那么控制服务器要指示其它结点撤消分布式事务的各个部分的影响。如果所有结点都回答准备好提交,控制服务器 则指示它们提交并等待它们的响应。等待确认信息阶段是第二阶段。
在接收到可以提交指示后,每个服务器提交分布式事务中属于自己的那一部分,并给控制服务器 发回提交完成信息。

  在一个分布式事务中,必须有一个场地的Server作为协调者(coordinator),它能向 其它场地的Server发出请求,并对它们的回答作出响应,由它来控制一个分布式事务的提交或撤消。该分布式事务中涉及到的其它场地的Server称为参 与者(Participant)。

事务两阶段提交的过程如下:
  ● 两阶段提交在应用程序向协调者发出一个提交命令时被启动。这时提交进入第一阶段,即预提交阶段。在这一阶段中:
  (1) 协调者准备局部(即在本地)提交并在日志中写入"预提交"日志项,并包含有该事务的所有参与者的名字。
  (2) 协调者询问参与者能否提交该事务。一个参与者可能由于多种原因不能提交。例如,该Server提供的约束条件(Constraints)的延迟检查不符合 限制条件时,不能提交;参与者本身的Server进程或硬件发生故障,不能提交;或者协调者访问不到某参与者(网络故障),这时协调者都认为是收到了一个 否定的回答。
  (3) 如果参与者能够提交,则在其本身的日志中写入"准备提交"日志项,该日志项立即写入硬盘,然后给协调者发回,已准备好提交"的回答。
  (4) 协调者等待所有参与者的回答,如果有参与者发回否定的回答,则协调者撤消该事务并给所有参与者发出一个"撤消该事务"的消息,结束该分布式事务,撤消该事务的所有影响。

  ● 如果所有的参与者都送回"已准备好提交"的消息,则该事务的提交进入第二阶段,即决策后提交阶段。在这一阶段中:
  (1) 协调者在日志中写入"提交"日志项,并立即写入硬盘。
  (2) 协调者向参与者发出"提交该事务"的命令。各参与者接到该命令后,在各自的日志中写入"提交"日志项,并立即写入硬盘。然后送回"已提交"的消息,释放该事务占用的资源。 
  (3) 当所有的参与者都送回"已提交"的消息后,协调者在日志中写入"事务提交完成"日志项,释放协调者占用的资源 。这样,完成了该分布式事务的提交。

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

     缺点

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

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

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

四 使用消息队列来避免分布式事务
  如果仔细观察生活的话,生活的很多场景已经给了我们提示。
  比如在北京很有名的姚记炒肝点了炒肝并付了钱后,他们并不会直接把你点的炒肝给你,往往是给你一张小票,然后让你拿着小票到出货区排队去取。
为什么他们要将付钱和取货两个动作分开呢?原因很多,其中一个很重要的原因是为了使他们接待能力增强(并发量更高)。

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

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

  有两种方法:

4.1.1 业务与消息耦合的方式

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

1
2
3
4
5
<span style="color: #000000;">Begin transaction
update A set amount</span>=amount-10000 where userId=1<span style="color: #000000;">;
insert into message(userId, amount,status) values(</span>1, 10000, 1<span style="color: #000000;">);
End transaction
commit;</span>

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

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

4.1.2 业务与消息解耦方式

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

  1)用户A在扣款事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送,只有消息发送成功后才会提交事务;

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

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

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

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

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

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

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

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

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

1
2
3
4
5
6
7
8
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;
分享到:
评论

相关推荐

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

    "浅谈分布式事务实现技术及应用场景探讨" 分布式事务是指在分布式系统中,多个节点之间的数据访问和更新操作集合,需要保证事务的原子性、一致性、隔离性和持久性。随着软件系统支持用户数的不断提高,对其架构的...

    浅谈常用的分布式事务选型

    本文将探讨几种常用的分布式事务处理方案,包括通用分布式事务规范XA、JAVA分布式事务规范(JTA)以及一些常见的分布式事务框架如2PC/3PC、TCC、MQ、Seata和Saga。 #### 二、分布式事务基础知识 ##### 2.1 事务的...

    浅谈分布式消息技术:Kafka.docx

    在Kafka的数据传输事务特点方面,有三种模式:at most once(最多一次)、at least once(至少一次)和exactly once(精确一次)。其中,at most once可能导致消息丢失,而at least once可能会导致消息重复,exactly...

    浅谈分布式锁

    为了实现最终一致性,分布式系统中常采用多种技术手段,包括分布式锁、分布式事务等。 分布式锁通常需要满足以下条件: 1. 可重入性:同一个线程可以多次获取同一把锁。 2. 性能要求:获取锁和释放锁的操作要高效,...

    浅谈分布式数据库系统架构.pdf

    3. 数据一致性:为了保证分布式事务的一致性,分布式数据库系统引入了分布式事务控制、集群快照等新技术。 在分布式数据库系统中,无共享的设计允许系统在理论上具备无限的横向扩展能力,同时保持高性能和全事务...

    浅谈分布式数据库架构.pdf

    设计分布式数据库时,需要考虑如何有效地进行数据分片、副本控制、负载均衡、异步复制、事务管理等关键问题。 在实际应用中,分布式数据库系统如Apache Cassandra、Google Spanner、Amazon DynamoDB等,已逐渐成为...

    浅谈分布式操作系统的论文

    这通常通过消息传递、远程过程调用(RPC)等方式实现,同时需要支持并发控制和事务处理,确保数据的一致性和完整性。 二、分布式系统的源头 分布式系统的概念源于对大型系统的需求和计算机网络的发展。早期的分时...

    浅谈分布式数据库管理应用系统的开发.pdf

    分布式数据库管理应用系统是信息化建设中的重要组成部分,尤其在当今大数据和云计算的时代背景下,它的作用愈发突出。系统设计和开发的目的是为了实现数据的高效、安全、一致的共享,同时确保资源的科学管理。 首先...

    浅谈分布式数据库的数据存储.pdf

    透明性体现在用户对数据存储位置的无知,数据的存储、查询和事务处理对用户来说是不可见的,而灵活性则表现在分布式数据库能够支持不同类型的存储需求和应用场景。 分布式数据库系统克服了传统集中式数据库的很多...

    浅谈分布式系统中的关联规则.pdf

    在分布式系统的研究领域中,关联规则挖掘是一项核心的技术,它涉及在大量数据中寻找不同项目之间的有趣关系。关联规则挖掘旨在发现事务数据库中项目之间的频繁模式、关联、相关性,或者在大型数据集中发现变量之间的...

    浅谈分布式数据库中数据分片与分配关系的比较.pdf

    数据分片与分配的合理设计,不仅影响数据访问的局部性,也直接影响到数据查询和事务处理的效率。 分布式数据库的设计中,数据分片与分配之间的关系处理是关键问题。数据分片的原则需要确保分片后的数据片断能够完整...

    浅谈银行分布式系统转型的几点体会.pdf

    分布式数据库除了必须具备分布式事务实时一致性、高并发的交易处理、在线数据重分布以及全局一致性的备份和恢复功能外,还需适应银行复杂的应用场景,满足实时一致性、高并发处理和数据一致性等业务需求。...

    浅谈PowerBuilder分布式技术在系统开发中的应用.pdf

    分布式应用的设计需要考虑如何有效地划分不同层之间的逻辑,如何保证不同部分之间的通信效率,以及如何处理分布式事务的一致性问题。为此,开发人员需要掌握更多的分布式设计原则、中间件技术,以及对网络通信、并发...

    浅谈分布式锁的几种使用方式(redis、zookeeper、数据库)

    通过在查询时加上FOR UPDATE关键字,可以锁定查询结果,直到当前事务结束。这种方式可以避免插入操作导致的非阻塞问题,但依然存在其他问题,需要额外处理。 接下来,我们讨论基于Redis的分布式锁。Redis是一个内存...

    浅谈J2EE框架和分布式网络管理.pdf

    J2EE基于Java技术,为多层分布式企业应用提供了标准的中间件规范,包括一组技术和服务。本文将详细介绍J2EE框架的构成、其主要优势以及与分布式网络管理的关系。 一、J2EE框架构成 J2EE框架主要包括以下几个层次: ...

    浅谈基于Postgres-XL的分布式地质大数据集群架构.pdf

    基于Postgres-XL的分布式地质大数据集群架构,是一种面向地质信息构建领域的分布式系统。在现代信息系统中,随着地质大数据的不断增加,如何高效地管理和处理这些数据成为一个挑战。Postgres-XL是PostgreSQL数据库的...

    浅谈Redis在分布式系统中的协调性运用

    有时是在一些特定的时间需要某个进程处理某些事务等等,人们通常会使用分布式锁、选举算法等技术来协调各个进程之间的行为。因为分布式系统本身的复杂特性,以及对于容错性的要求,这些技术通常是重量级的,比如 ...

    浅谈oracle rac和分布式数据库的区别

    - 分布式数据库:在分布式系统中,由于数据分布在不同节点,事务的ACID(原子性、一致性、隔离性、持久性)属性需要跨节点协调,可能引入两阶段提交等复杂机制,以确保数据一致性,这可能影响性能。 3. 故障恢复和...

    浅谈SOAP.pdf

    ### 浅谈SOAP知识点解析 #### 一、为什么需要SOAP? 随着信息技术的飞速发展,企业面临着日益复杂的信息化挑战。为了适应这种变化,现代企业信息系统往往由多个平台和技术构成,形成了复杂的多系统环境。这样的...

Global site tag (gtag.js) - Google Analytics