在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此叫数据库事务。由于应用主要靠关系数据库来控制事务,而数据库通常和应用在同一个服务器,所以基于关系型数据库的事务又被称为本地事务。
在分布式系统中,一个操作如果跨越多个服务或者多个数据库的话。本地事务是无法回滚其他服务或其他数据库的事务的。因此分布式事务就随之出现了。
当一个业务跨越多个服务或数据库的时候,我们要保证的是,这多个服务要么全部成功,要么全部失败,这就是分布式事务要实现的目标。
在分布式系统中无法同时满足这三个指标,这个结论就叫做CAP定理。
Consistency(一致性)
用户访问分布式系统中的任意节点时,得到的数据必须一致。
Availability(可用性)
用户在访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝。
由上图可以看出node02的数据还未同步到node03,如果这时有请求到node03的话就会阻塞或拒绝,直到数据同步完成,这样的话满足了一致性,但是却不能满足可用性。如果我们直接将node03上未同步的旧数据返回个用户,或者返回默认数据的话,这样就满足了可用性,但是却不能满足一致性了。如果两者都要满足的话,那么node03会自己形成一个独立分区,将于其他两个节点失去联系,其他两个节点能同时满足可用性和一致性。
Partition tolerance(分区容错性)
CAP定理总结:
在分布式系统中,分区是不可避免要出现的,我们不能让系统在出现分区的时候无法对外提供服务。因此,分区容错性是必须要满足的。因此CAP定理的主要问题还是C和A无法同时满足。
如果要满足A(可用性),那么当某个节点数据还未完成同步的话,也必须能够快速响应而不是超时或拒绝,这时候返回的数据可能是同步前的数据或默认数据,数据的一致性就无法得到保障。
如果要满足C(一致性),那么当某个节点还未完成同步的话,就必须等待同步完成,才返回数据。在等待的过程中,可能会超时,因此就无法保证系统的可用性。
BASE理论是对CAP的一种解决思路,在A和C之间进行调和。
常见的分布式解决方案有2PC、TCC、可靠消息最终一致性、最大努力通知等。
举例:
张三和李四好久不见,老友约起聚餐,饭店老板要求先买单,才能出票。这时张三和李四分别抱怨近况不如意,囊中羞涩,都不愿意请客,这时只能AA。只有张三和李四都付款,老板才能出票安排就餐。
准备阶段:老板要求张三付款,张三付款。老板要求李四付款,李四付款。
提交阶段:老板出票,两人拿票纷纷落座就餐。
例子中形成了一个事务,若张三或李四其中一人拒绝付款,或钱不够,店老板都不会给出票,并且会把已收款退回。
XA规范是X/Open组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。
传统的XA模式只有TM、RM两种角色,如下图。而Seata的XA模式做了一些封装,多了一种TC角色,使系统更加的健壮。
优点
缺点
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷。
工作模型
举例:
AT模式脏写问题
简而言之,就是A事务回滚的时候,通过日志恢复数据前,数据已经被B事务修改了,这时候再恢复数据,就会导致B事务修改的数据被覆盖。问题出现的原型就是没有做好事务的隔离性。
AT模式的写隔离
为了解决脏写问题,Seata引入了全局锁的概念。A事务正在操作某行数据的时候,会获得全局锁。B事务想修改该数据的时候发现该数据被A锁着,这样就会等A事务完成后释放锁了,B事务才能接着操作。
上图的事务1和事务2都是由seata管理的,如果事务2不是由seata管理的呢,这时候该怎么避免脏写问题呢?其实seata在阶段一保存快照的时候会把更新前和更新后的数据都保存,在阶段二回滚的时候会比较数据和更新后的数据是否一致,一致的话就回滚,不一致就不会滚了,如下图。
优点
缺点
TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复,不需要加锁,性能比AT模式要好很多。需要实现三个方法:
TCC模式的优点
TCC模式的缺点
工作模型
TCC模式的空回滚和业务悬挂
总而言之,空回滚就是未执行try就执行cancel了。业务悬挂就是执行完cancel后又执行try了。
如何保证消息的可靠性
参考 微服务入门到入土(08)-消息队列RabbitMQ
最大努力通知,也是依靠消息队列来实现的。只不过不能保证消息一定能发出去或者说不能保证消息一定能被消费。但是消息的生产者必须提供一个查询接口,以供消息发不出去的时候,让消费者可以自主查询。
比如支付服务,App调用微信的支付功能的时候,微信未通知调用发是否支付成功,同时也提供了对账功能,供用户查询是否支付成功,这样用户就算没有收到支付成功的通知,也可以自己去查询。