一组批量的SQL语句(insert,update,delete)执行来操作数据库数据,使用事务来处理,保证数据的完整性,要么全部执行,要么全部不执行。这组批量SQL可视为一个完整的逻辑处理工作单元。
比如:转账,借钱的批量更新操作,删除用户所有信息级联删除的操作。
那么事务应满足四个条件(ACID):
原子性-Atomicity
事务作为一个整体(不可分割的工作单元),要么全部成功(commit),要么全部失败(rollback)。不可能只执行其中的一部分操作。
一致性-Consistency
事务在开启之前和结束之后,数据的完整性没有被破坏。
隔离性-Isolation
一个事务的执行,不影响其他事务的执行。即:并发执行的事务之间不能互相干扰。
持久性-Durability
已提交的事务对数据的操作是永久的。
分布式系统CAP原则包括:Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性)。
分布式事务是解决分布式系统中数据一致性的常用方案。
Two-phase commit protocol 二阶段提交
XA协议由X/Open组织(最先是Tuxedo)提出的资源管理器(数据库)与事务管理器的接口标准。XA协议采用两阶段方式来管理分布式事务。
JTA(Java Transaction API)定义了Java的XA协议接口(并没有实现),由各个厂商实现JAT接口。
回到重点:二阶段提交引入一个事务协调者来管理参与者(各微服务)的提交或回滚。
2PC存在的缺陷:
为了解决2PC的一些缺陷,3PC(准备阶段,预提交阶段,提交阶段)出现了,在2PC的基础上多了一个预提交阶段。但整体的交互过程更长了,且还是会出现数据不一致的问题。
3PC中参与者也引入了超时机制,在等待超时后协调者和参与者会中断事务,与2PC中只有协调者才有超时机制相比,降低了阻塞时间。但是治标不治本啊,在第三阶段发给某一协调者的消息超时,还是会出现数据不一致的情况。
TCC(Try - Confirm - Cancel)与2PC,3PC(数据库层面)不同,TCC是业务层面的分布式事务。也称为补偿事务。针对每一个操作,都注册一个与其对应的确认和撤销(补偿)操作。
Try
对资源做预留和锁定。
Confirm
确认操作
Cancel
撤销。将Try阶段的操作撤销。
TCC的缺点:
在Confirm或Cancel阶段,都有可能失败。这样一来,会额外的编写撤销代码融入到业务中,对业务入侵较大。并且有些特定的业务场景,失败后需要人工介入。
使用系统本地的事务来实现分布式事务(将一个大的分布式事务分解为若干微服务中的小事务)。
首先需要在本地新建一张消息表(与业务数据表处于同一个库),利用本地的事务特性保证对这两个表的操作,使用消息队列将消息表中的消息进行通知,调用下一个操作。调用成功,则更新消息表中的状态即可。调用失败,后台定时任务会定时读取本地消息表,将未成功的消息再调用对应的服务,等调用成功更新状态。存在消息对应的操作不成功,都会重试。这里要保证对应服务的方法是幂等。一般重试会有最大次数,超过了最大次数会有人工处理。
这样一来,本地消息表是保证了数据的最终一致性。
但是,这样严重依赖于数据库的消息表来管理事务,频繁的读写给数据库也带来了压力,高并发下也会有瓶颈。
直接使用MQ(RocketMQ)来实现事务,不再使用本地消息表。
Rocket MQ 中文文档
首先A服务向Brock发送事务消息,这个事务消息对服务B不可见(Half Message)。
服务A确认消息发送成功后,开始执行本地事务。根据执行事务的结果给Broker回复。
事务执行成功,A发送Commit至Broker,则B服务可以消费该事务消息;