龙空技术网

基于分布式事务彻底解决数据库数据一致性

Java技术人 157

前言:

此刻同学们对“java显示数据库数据”大约比较注重,我们都需要了解一些“java显示数据库数据”的相关资讯。那么小编也在网络上汇集了一些有关“java显示数据库数据””的相关资讯,希望朋友们能喜欢,看官们一起来学习一下吧!

基本概念:

数据一致性定义 是任务人、任何时间、任何地点、任何接入方式、任何服务数据都是一致性的;数据不一致产生原因一般都有几点,例如数据分散在多处(多个DB,或者是DB和缓存),再来看一个 二手交易平台案例,它有用户、商品、交易等功能,如下图所示,所以如果控制不好往往会出现数据不一致的现象

一、案例驱动

1、电商平台购买商品过程 下单->减库存->支付

一个请求分布在多个数据库上面,此时本地事务已经不奏效,要用分布式事务去解决

二、分布式事务场景

1、电商下单场景

① 下单

② 发送消息到MQ

2、一致性保证

① 本地事务

(1)下单操作;

(2)发送MQ消息操作;

(3)放进一个本地事务;

三、分布式事务分类

1、刚性分布式事务有 强一致性、 XA模型、 CAP中的CP

① 满足传统事务特性

(1)ACID(Atomicity-原子性、Consistency-一致性、Isolation-隔离性、Durability-持久性)

XA模型

(1)XA是X/Open CAE Specification(Distributed Transaction Processing)模型中定义,XA规范由AP、RM、TM组成;

(2)其中应用程序(Application Program 简称AP);AP定义事务边界(定义事务开始和结束)并访问事务边界内的资源;

(3)资源管理器(Resource Manager 简称RM);RM管理计算机共享的资源,资源即数据库等;

(4)事务管理器(Transaction Manager 简称TM);负责管理全局事务,分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚、失败恢复等;

② 两阶段提交

(1)过程

* 两阶段提交是XA规范标准的实现;

* TM发起prepare投票;

* RM都同意后,TM再发起commit;

* commit过程出现宕机等异常,节点服务重启后,根据XA recover再次进行commit补偿;

(2)缺点

* 同步阻塞模型;

* 数据库资源锁定时间过长;

* 全局锁(隔离级别串行化),并发低;

* 不适合长事务场景

2、柔性分布式事务

① CAP

分布式环境下P一定需要,CA权衡折中,柔性事务中会选择A

② BASE理论

* Basically Available -基本可用

* Soft state -柔性状态

* Eventual consistency -最终一致性

③ 架构考虑

* 柔性事务是对XA协议的妥协,它通过降低强一致性要求,从而降低数据库资源锁定时间,提升可用性;

④ 典型架构实现

TCC模型

(1)Try-Confirm-Cancel

(2)TCC模型完全交由业务实现,每个子业务都需要实现Try-Confirm-Cancel接口,对业务侵入大,它最大的特点就是资源的锁定交由业务方;

(3)Try:尝试执行业务,完成所有业务检查,预留必要的业务资源;

(4)Confirm:真正执行业务,不再做业务检查;

(5)Cancel:释放Try阶段预留的业务资源;

(6)汇款服务、收款服务案例:A用户向B用户汇款500元;

汇款服务

* Try:

a 检查A账号有效性,即查看A账号的状态释放为“转账中”或“冻结”;

b 检查A账号余额是否充足;

c 从A账号中扣减500元,并将状态置为“转账中”;

d 预留扣减资源,将从A往B账号转账500元这个事件存入消息或日志中;

* Confirm:

a 不做任何操作,因为在这个案例中这个动作在Try中的c已经做了

* Cancel:

a A 账号增加500元

b 从日志或者消息中,释放扣减资源

收款服务

* try:

a 检查B账号是否有效;

* Confirm:

a 读取日志或者消息,B账号增加500元;

b 从日志或者消息中,释放扣减资源;

* Cancel:

a 不做任务操作

Saga模型

(1)起源于1987年Hector&Kenneth发表的论文Sagas;

(2)Saga模型把一个分布式事务拆分为多个本地事务,每个本地事务都有相应的执行模块和补偿模块(对于TCC中的Confirm和Cancel);

(3)当Saga事务中任意一个本地事务出错时,可以通过调用相关的补偿方法恢复之前的事务,达到事务最终一致性;

(4)当每个Saga子事务T1,T2,...,Tn 都有对应的补偿定义C1,C2,...,Cn-1,那么Saga系统可以保证

* 子事务序列T1,T2,...,Tn得以完成(最佳情况)

* 或者序列T1,T2,...,Tj,Cj-1,...,C2,C1, 0<j<n,得以完成;

(5)Saga隔离性

* 业务层控制并发,在应用层加锁,应用层预先冻结资源等;

(6)Saga恢复方式

* 向后恢复:补偿所有已完成的事务,如果任一子事务失败;

* 向前恢复:重试失败的事务,假设每个子事务最终都会成功;

四、分布式事务对比(刚性分布式事务VS柔性分布式事务)

(1)柔性分布式事务通用处理思路

* 本地事务->短事务

* 分布式事务->长事务

* 转变成多个短事务

* 案例:A【下单】->B【减库存】->C【支付】

A ->DB1;B->DB2;C->DB3;A/B/C都成功;A/B成功,C失败就进行补偿;

(2)柔性分布式事务 业务场景

① 异步场景:基于MQ消息驱动分布式事务;

方案一:业务方提供本地操作成功回查功能

* 事务消息:MQ提供类似X/Open XA的分布式事务功能,通过MQ事务消息能达到分布式事务最终一致;

* 半消息:暂不能投递的消息,发送方已经将消息成功发到了MQ服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即为半消息;

* 消息回查:由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,MQ服务端通过扫描发现某条消息长期处于“半消息”时,需要主动向消息生产者询问该消息的最终状态(Commit或是Rollback),即消息回查;

MQ分布式事务设计方案

* MQ事务消息设计事务消息作为一种异步确保性事务,将两个事务分支通过MQ进行异步解耦,MQ事务消息的设计流程同样借鉴了两阶段提交理论,整体交互流程如下图所示:

1.事务发起方首先发送prepare消息到MQ;

2.在发送prepare消息成功后执行本地事务;

3.根据本地事务执行结果返回commit或者是rollback;

4.如果消息是rollback,MQ将删除该prepare消息不进行下发,如果是commit消息,MQ会将消息发送给consumer端;

5.如果执行本地事务过程中,执行端挂掉或者超时,MQ服务器端将不停的询问producer来获取事务状态;

6.Consumer端的消费成功机制有MQ保证;

方案一总结:

优点:通用,

缺点:

1、业务方需提供回查接口,对业务侵入大;

2、发送消息非幂等;

3、消费端需要处理幂等;

4、MQ服务端需要提供半消息功能,需要定时对半消息进行遍历的功能;

方案二:本地事务消息表

* 本地操作和发送消息通过本地事务强一致性

a 本地事务操作表

b 本地事务消息表 mqMessages(msgid,content,topic,status)

方案二:本地事务消息表总结

(1)发送端消息不幂等,最少发发送一次;

(2)消费端处理消息幂等(利用分布式锁)

(3)A->B->C

A/B成功,C失败

* 记录错误日志;

* 报警;

* 人工介入;

(4)优点:业务侵入小

② 同步场景:基于异步补偿分布;

架构设计的三大关键

总体架构设计

业务逻辑层Proxy设计(基于AOP实现)

1、逻辑层调用上加上事务注解@Around("execution(**(..))&&@annotation(TX)")

2、Proxy在真正业务逻辑被调用之前,生成一个全局唯一TXID标识事务组,TXID保存在ThreadLocal变量里,方法开始前写入,完成后清除,并向远端数据库写入TXID并把事务组置为开始状态;

3、业务逻辑层调用数据访问层之前,通过PRCProx代理记录当前调用请求参数;

4、如果业务正常,调用完成后,当前方法的调用记录存档或删除;

5、如果业务异常,查询调用链反向补偿;

数据访问层设计

1、原子接口

2、补偿接口

① 业务方来提供

② 幂等性保证

3、基于原则接口方法,在方法名上加注解标注补偿方名@Compensable(cancelMethod="cancelRecord")

分布式事务补偿服务

1、事务组表(数据库表TDB)

① 记录事务组状态;

② txid、state、timestamp

2、事务调用组表(数据库表TDB)

① 记录事务组内的每一次调用以及相关参数;

② txid、actionid(操作步骤)、callmethod、pramatype、params

3、补偿策略

① 调用执行失败、修改事务组状态;

② 分布式事务补偿服务异步执行补偿;

例如向上面的一个下单的操作:A【下单】->B【减库存】->C【支付】,当进行这一操作的时候会在事务组表里面新增一条记录

state: 1:代表下单触发业务;2:代表成功下单支付完成;3:代表失败;4:代表补偿成功;5:代表补偿失败;

在事务调用组表里有相应三条记录

所以对应它的一次下单操作,它有相应的三个本地事务的执行逻辑,actionid标识着这个下单操作内部的逻辑顺序,当整个下单操作成功时,会将事务组表里面的state更新为2成功,如果失败更新为3然后由分布式事务补偿服务按照事务调用组表里面的该事务001对应的顺序进行执行回滚补偿方法,回滚成功则将事务组表里面的state更新为4,回滚失败更新为5,这个时候就要介入人工操作了;

成功案例

失败案例

如果分布式事务本身补偿失败,则进行

1、记录错误日志;2、报警;3、人工介入

标签: #java显示数据库数据