分布式事务简介

还是一样,在这里帮之前的同事大牛宣传一下,顺便分享给大家微笑

昨天讲过了什么是分布式系统。在分布式系统中,有一个永远也绕不开的问题,那就是事务问题。随着互联网的普及,网民数量的增加,各类网站纷纷采用分布式或微服务进行系统架构,这使得分布式事务的问题越发突出。这次我们聊一聊在分布式系统中如何实现事务。

什么是事务

百度百科中的介绍:

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。


事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。


原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。


一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。


隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。


持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

事务在传统集中式系统中的应用

在传统的集中式系统中,一个系统一般只会有一个数据源,就算出现两个或以上的数据源,也会尽可能避免出现跨库事务的发生,所以我们这里只以单数据源为例。

集中式系统的事务主要是通过数据库的ACID来实现:

TransactionManager transactionManager = ...; // 创建一个事务管理器  

transactionManager.begin(); // 启动事务  

try {  

    jdbcConn.executeUpdate(sql); // 执行SQL语句  

    transactionManager.commit(); // 提交事务

} catch(Throwable t) {  

    transactionManager.rollback(); // 回滚事务  

}  


这是一个简单的手动事务的实现,当然全能的spring通过aop的方式帮我们实现了通过@Transactional来实现自动化的事务。

什么是分布式事务

我个人的理解,传统事务就在同一物理媒介(如:磁盘、内存)上保证数据的正确性,即:ACID,要么同时成功,要么同时失败。

而分布式事务是在不用的物理媒介,甚至是不同的网络环境中保证数据的正确性。所以既然是不同的物理媒介,那我们传统的通过数据库事务控制就没有办法达到我们想要的结果了。


其实在我的理解中,分布式事务已经不属于事务的范畴,而是我们通过一些工具或者手段来保证数据的ACID。

事务在分布式系统中的应用

我们举一个最典型的电商支付的栗子:


这是一个简单的电商支付流程:

1、电商系统在用户下单后需要锁定库存,锁定优惠券、锁定积分,等待支付系统返回支付结果。由于库存中心、运营中心、积分中心都是分布式体系中的子模块,所以这里已经涉及到分布式事务。

2、支付系统在收到支付请求后需要校验支付规则,新增支付订单,等待银行系统返回扣款结果。由于查询操作比较多,这里并不涉及到分布式事务。

3、银行系统处理完成后将扣款结果返回支付系统,支付系统需要更改支付订单状态,生成结算数据,通知电商系统,这里也会涉及到分布式事务。

4、电商系统收到支付系统的支付结果,需要更改交易订单状态,扣除库存,扣除积分,使用优惠券,增加积分,通知仓库发货,这里也会有分布式事务。

那我们如何来保证上述流程中的数据正确性呢?目前业界普遍比较认可的分布式事务处理方案主要有3种:


1、TCC(Try/Confirm/Cancel)分布式事务、两阶段提交

TCC事务和两阶段提交基本差不多,我们这里就单说一下两阶段提交。

所谓的两个阶段是指:第一阶段:准备阶段和第二阶段:提交阶段。如下图:


准备阶段:事务协调者(事务管理器)给每个参与者(资源管理器)发送Prepare消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事务,写本地的redo和undo日志,但不提交,到达一种“万事俱备,只欠东风”的状态。

提交阶段:如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。

还是阿姨的栗子:

天天加班的你明天终于要休息了,想找一个阿姨来打扫卫生,一个阿姨来洗衣服,毕竟我们都是分布式阿姨嘛。然后你通知阿姨A,明天来打扫卫生;阿姨B,明天来洗衣服。两个阿姨都答应了,那准备阶段就完成了。第二天,如果两个阿姨都来了,那就愉快的开始打扫了,如果阿姨A突然打电话说临时有事不来了,那你就需要赶紧打电话给阿姨B,让她也别来了,我们改日再约,这就是提交阶段了。

两阶段提交还是存在着一些问题,比如事务管理器的单点问题,性能问题等,为了解决这些问题,大牛们在两阶段的基础上,提出了三阶段提交,这里我们就不详细说明了。

更改订单状态,扣除库存,扣除积分,使用优惠券,增加积分这些必须要确保数据强一致性的场景可以使用这种事务模式。

2、异步确保型事务

在一些对数据实时性要求不高的场景下,我们可以通过一些异步的手段保证数据的最终一致性。如:MQ,缓存等。

还是阿姨:

有一天阿姨来做家务,可是迟到了1个小时,导致家务还没做完,然后阿姨答应明天再来免费给你补一个小时。你给中介公司打了电话,让他们明天催着阿姨来补完这一个小时。第二天,中介公司每个小时一个电话催着阿姨,赶紧去补一个小时啊,直到阿姨来到你家,做完了这一个小时。

生成结算数据之类的场景可以使用这种事务模式,因为对数据的实时性要求不高,只要保证最终一致性即可。

3、最大努力通知型事务

对于一些有可能因为各种异常原因(如:断电、网络中断等)而无法确保做到数据一致性的情况,我们可以用以通知为主,补偿为辅的手段来保证数据最终一致性。

继续阿姨:

临时接到明天要去出差通知的你刚好约了明天阿姨来做家务,没办法,只能通知阿姨明天别来了,可是给阿姨打电话老是关机,打了一天都是关机。没招了,第二天你临出门之前在门口留了张字条,表示自己出差,让阿姨改日,阿姨来了以后看到了字条就回去了。

等待返回结果这类的场景可以采用这种事务模式,如果通知不到还可以提供一个查询接口来查询结果。

三种分布式事务模式对比


同事的地址博客。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值