目录
ACID
单机数据库事务ACID
- Atomic(原子性) 事务必须是原子的工作单元
- Consistent(一致性) 事务完成时,必须使所有数据都保持一致状态
- Isolation(隔离性) 并发事务所做的修改必须和其他事务所做的修改是隔离的
- Duration(持久性) 事务完成之后,对系统的影响是永久性的
为什么会产生分布式事务
- SOA
- 分库分表
下单需要rpc调用支付服务和扣减库存服务,已经不在一个服务进程内,且涉及操作多个db
传统的ACID已经无法满足,因此引入了分布式事务
要么支付和扣减库存一起成功,要么一起失败,不会出现数据不一致性,不允许既有成功又有失败的中间状态
刚性分布式事务
数据强一致性
CP模型
【XA模型】
可以说XA是一种规范
衍生的2种方案,2阶段提交和三阶段提交
-2PC 两阶段提交-
整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段
成功的情况
失败的情况
2pc存在的缺陷:
1、同步阻塞:最大的问题及同步阻塞,所有参与事务的逻辑均处于阻塞状态。
2、单点:协调者存在单点问题,如果协调者出现故障,参与者将一直处于锁定状态。
3、脑裂:在阶段2中,如果只有部分参与者接收并执行了Commit请求,会导致节点数据不一致
2pc很少用,性能差,存在很多问题
-3PC 三阶段提交-
在询问的时候并不锁定资源,除非所有人都同意了,才开始锁资源。
柔性分布式事务
最终一致性、并发性高,性能更好
满足AP
CAP 、BASE理论
通过降低数据一致性,降低数据库资源的锁定时间,保证了CAP中的A可用性
且需要补偿接口保证数据最终一致性
Try阶段:
完成所有业务检查(一致性),预留业务资源(准隔离性)
Confirm阶段:
确认执行业务操作,不做任何业务检查, 只使用Try阶段预留的业务资源。
Cancel阶段:
取消Try阶段预留的业务资源。
TCC两阶段提交与XA两阶段提交的区别是:
XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。
XA事务中的两阶段提交内部过程是对开发者屏蔽的。事务管理器在两阶段提交过程中,从prepare到commit/rollback过程中,资源实际上一直都是被加锁的。如果有其他人需要更新这两条记录,那么就必须等待锁释放。
TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。
TCC中的两阶段提交并没有对开发者完全屏蔽,也就是说从代码层面,开发者是可以感受到两阶段提交的存在。开发者需要提供try、confirm、cancel三个接口。开发者明显的感知到了两阶段提交过程的存在。
try、confirm/cancel在执行过程中,一般都会开启各自的本地事务,来保证方法内部业务逻辑的ACID特性。
其中:
1、try过程的本地事务,是保证资源预留的业务逻辑的正确性。
2、confirm/cancel执行的本地事务逻辑确认/取消预留资源,以保证最终一致性,也就是所谓的补偿型事务
(Compensation-Based Transactions)。
由于是多个独立的本地事务,因此不会对资源一直加锁。
- 每个Saga由一系列sub-transaction Ti 组成
- 每个Ti 都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果 (补偿回滚)
- T1, T2, T3, …, Tn
- T1, T2, …, Tj, Cj,…, C2, C1,其中0 < j < n
- backward recovery,向后恢复,补偿所有已完成的事务,如果任一子事务失败。即上面提到的第二种执行顺序,其中j是发生错误的sub-transaction,这种做法的效果是撤销掉之前所有成功的sub-transation,使得整个Saga的执行结果撤销。
- forward recovery,向前恢复,重试失败的事务,假设每个子事务最终都会成功。适用于必须要成功的场景,执行顺序是类似于这样的:T1, T2, …, Tj(失败), Tj(重试),…, Tn,其中j是发生错误的sub-transaction。该情况下不需要Ci。 (重试策略,保证所有子事务都成功,以至于确保分布式事务的成功)
RocketMQ 事务消息功能实现分布式事务
通过发送半消息实现
MQ先发送半消息,MQ server端返回ACK,此时消息只是到MQ server,还没有发送给消费者
执行本地事务,本地事务执行成功,给MQ server 发送commit,真正触发消息投递到消费者
如果commit消息丢了,没有发送到MQ server 怎么办?
业务方需要提供一个反查本地事务状态的接口,定时去反查接口,来决定是commit或rollback
Half消息,就类似prepare
本地消息表实现分布式事务
用户注册后,即送皮肤
用户注册是操作在DB1,送皮肤是操作在DB2
不是简单的本地事务
分布式事务保证操作DB1、 DB2都成功
我们引入事件表 + MQ 解决分布式事务
什么是事件表,就是记录用户注册/送皮肤这个事件的状态
用户注册成功后,也要向user_evernt表插入一个对应的事件数据,并置当前状态为NEW
然后定时任务1 ,user_evernt扫表,取出NEW的数据进行送皮肤操作,
把送皮肤的消息发送到MQ,发送后把user_evernt状态置为PUBLISHED
处理送皮肤的服务接收到消息后,向reward_evernt表插入一条数据,并把状态置为PUBLISHED
然后定时任务2,reward_evernt扫表,取出PUBLISHED的数据进行操作,并把状态置为DONE
1,先写用户表,再写事件表( NEW),事件表内容content就是 用户ID + 奖励皮肤
2,定时任务扫事件表,取出NEW状态下的数据,把content发送到activeMQ,并把状态置为PUBLISHED
3,消费者接收消息后,写事件表,content就是从activeMQ中读到的消息,即用户ID + 奖励皮肤,状态为PUBLISHED
4,定时任务扫奖励事件表,取出状态为PUBLISHED的数据,然后再reward表新加一条数据,再把事件表中对应的数据状态置为PROCESSED
整个过程完成
这样就保证了两个数据源之间数据的最终一致性了
说点什么
您将是第一位评论人!