微服务分布式事务及微服务分布式事务解决方案

Yanyan 794 2023-10-23

微服务架构下的分布式事务

微服务分布式事务及微服务分布式事务解决方案

一提到分布式事务,就不得不说XA规范、TCC事务模型。

XA规范
分布式事务中耳熟能详的全局事务管理器(Transaction Manager)以及资源管理器(Resource Manager)。就是XA规范定义的。
XA协议提供了资源管理器(数据库)和事务管理器之间的接口标准。XA接口是双向的系统接口,在事务管理器和一个或者多个资源管理器之间建成通信桥梁,事务管理器充当全局事务中的协调者,控制着全局事务,管理事务的生命周期,协调资源, 一般常见的事务管理器(TM)有JDBC、hibernate框架中的TransacManager等。资源管理器控制和管理实际的资源,比如数据库。目前各大数据库产家都支持这种标准。
XA 事务的执行流程 XA
两阶段提交的分布式事务,XA将一次事务分割成两个阶段,Prepare准备阶段和Commit提交阶段。
Prepare阶段,TM向所有的RM发送prepare指令,RM接收到指令以后,就会执行数据的修改和日志记录操作(binlog和redo log)。然后返回能不能提交的消息给TM。TM假如接收到所有的参与者返回的消息,会通知所有的RM进行提交,于是进入第二阶段。
Commit阶段,TM接收到所有事务参与者RM返回的Prepare结果,如果RM全部返回提交,则事务进行提交,假如存在某个RM返回不能提交或者超时的情况,则所有的事务进行回滚操作。
MySQL如何实现XA规范
大多数生产环境中的Mysql都会开启Bin log日志,于是为了为了两套日志的数据一致性,Mysql就是使用了XA事务。
当事务提交的时候,Binlog中会添加一个XID_EVENT作为事务的结束,该事件记录了事务的ID就是XID。
Binlog事务的提交过程,是先写redo log,再写binlog,以binlog写成功为事务提交成功的标志。
事务提交:
第一步,prepare阶段,写入redo log。binlog不做任何操作。
第二步,写入binlog。
第三步,事务提交。
第一步和第二步失败,整个事务会回滚,第三步失败,事务会进行再次提交。
Spring事务
众所周知,Spring事务采用AOP的方式实现,Spring中的事务机制和业务代码几乎不存在耦合关系。
获取事务的属性(@Transactional注解中的配置,比如spring的事务传播机制)
加载spring 配置中的事务管理器PlatformTransactionManager。
获取事务的信息TransactionInfo
执行目标方法
提交或者回滚
Spring 提供了超类PlatformTransactionManager,Hibernate自定义实现了自身框架的事务管理器HibernateTransactionManager。HibernateTransactionManager中会打开sessionFactory,创建Session,执行事务,关闭session。session在打开过程中还存在Hibernate的一级缓存,查询数据的时候会先进行session一级缓存的查询,然后在进行DB的查询。
spring事务的传播级别
PROPAGATION_REQUIRED 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 Spring 默认的事务的传播。 PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚之后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也可以不处理回滚操作。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
spring事务不生效的场景
private、final、static方法,事务不生效,入口方法必须是public ,spring的AOp特性决定的,spring认为private自己用的方法应该自己控制,不应该用事务切进去
在同一个类中一个无事务的方法调用另一个有事务的方法,事务是不会起作用的
如果使用的是rollbakfor的默认,已检查的异常(所有派生自Error和RuntimeException的类,都是未检查异常.其余的是已检查异常, 比如nullPointException是未检查的,IllegalAccessException 是已检查的)不回滚, 可设为rollbackFor={Exception.class}
TCC事务模型
TCC提出了一种新的事务模型,是基于业务层面的事务定义,锁粒度完全由业务自己控制,目的是解决复杂业务中,跨表跨库等大颗粒度资源锁定的问题。TCC把事务运行过程分成Try、Confirm/Cancel两个阶段,每个阶段的逻辑由业务代码控制,避免了长事务,可以获取更高的性能。
Try 阶段: 调用 Try 接口,尝试执行业务,完成所有业务检查,预留业务资源。Confirm 或 Cancel 阶段: 两者是互斥的,只能进入其中一个,并且都满足幂等性,允许失败重试。
Confirm 操作: 对业务系统做确认提交,确认执行业务操作,不做其他业务检查,只使用 Try 阶段预留的业务资源。Cancel 操作: 在业务执行错误,需要回滚的状态下执行业务取消,释放预留资源。
Try 阶段失败可以 Cancel,如果 Confirm 和 Cancel 阶段失败了怎么办?
TCC 中会添加事务日志,如果 Confirm 或者 Cancel 阶段出错,则会进行重试,所以这两个阶段需要支持幂等;如果重试失败,则需要人工介入进行恢复和处理等。
实际开发中,TCC 的本质是把数据库的二阶段提交上升到微服务来实现,从而避免数据库二阶段中长事务引起的低性能风险。
所以说,TCC 解决了跨服务的业务操作原子性问题,比如下订单减库存,多渠道组合支付等场景,通过 TCC 对业务进行拆解,可以让应用自己定义数据库操作的粒度,可以降低锁冲突,提高系统的业务吞吐量。
TCC 的不足主要体现在对微服务的侵入性强,TCC 需要对业务系统进行改造,业务逻辑的每个分支都需要实现 try、Confirm、Cancel 三个操作,并且 Confirm、Cancel 必须保证幂等。
为了应用 TCC 事务模型,需要对业务代码改造,抽象 Try、Confirm 和 Cancel 阶段。
Try 操作一般都是锁定某个资源,设置一个预备的状态,冻结部分数据。比如,订单服务添加一个预备状态,修改为 UPDATING,也就是更新中的意思,冻结当前订单的操作,而不是直接修改为支付成功。库存服务设置冻结库存,可以扩展字段,也可以额外添加新的库存冻结表。积分服务和库存一样,添加一个预增加积分,比如本次订单积分是 100,添加一个额外的存储表示等待增加的积分,账户余额服务等也是一样的操作。
Confirm 操作就是把前边的 Try 操作锁定的资源提交,类比数据库事务中的 Commit 操作。在支付的场景中,包括订单状态从准备中更新为支付成功;库存数据扣减冻结库存,积分数据增加预增加积分。
Cancel 操作执行的是业务上的回滚处理,类比数据库事务中的 Rollback 操作。首先订单服务,撤销预备状态,还原为待支付状态或者已取消状态,库存服务删除冻结库存,添加到可销售库存中,积分服务也是一样,将预增加积分扣减掉。
业务请求过来,开始执行 Try 操作,如果 TCC 分布式事务框架感知到各个服务的 Try 阶段都成功了以后,就会执行各个服务的 Confirm 逻辑。
如果 Try 阶段有操作不能正确执行,比如订单失效、库存不足等,就会执行 Cancel 的逻辑,取消事务提交。
关于TCC的事务模型,国内存在很多的开源框架。如ByteTCC,TCC-transaction,Himly等。
比较下XA事务和TCC事务的区别
2PC/XA 是数据库或者存储资源层面的事务,实现的是强一致性,在两阶段提交的整个过程中,一直会持有数据库的锁。
TCC 关注业务层的正确提交和回滚,在 Try 阶段不涉及加锁,是业务层的分布式事务,关注最终一致性,不会一直持有各个业务资源的锁。
业内还存在一种3PC的分布式事务,与2PC不同的地方是引入了超时机制,在第一阶段和第二阶段中新增了一个准备阶段。这里的Can-Commit是尝试获取数据库锁,第二阶段和第三阶段同2PC中一致,


Java互联网架构-微服务架构如何解决跨库问题的思路与方案?

一般来说如果应用系统出现性能瓶颈或者业务代码耦合过重,可考虑使用微服务架构,而后端的数据库通常使用读写分离,双主互备或者是分库分表来实现性能的提升和数据服务的高可用。

在数据分布在不同的数据库服务器的带来良好性能的同时,新的问题也随之而来,比如说数据一致性的保证,性能监控,数据存取复杂等,而较为突出的就是数据跨库问题!数据分布在不同的节点上,导致原来的连接查询需要跨库,字段的主键难以保证唯一,跨库的事务处理复杂,下面逐一解决:

1,连接查询(join)问题:因为库表分布在不同的机器上,连接查询失效。

解决办法:

①,代码解决:根据某个字段进行hash的方式进行分库分表,保证落在一个库中的类似表中(比如aa_00.t_user_0000和aa_00.t_member_0000),然后基于这样的规则在代码中进行连接查询语句书写!

②,同步:将常用的,需要的字段同步到一个库中进行联合查询!

③,冗余:在一个库中冗余更多的连接查询需要的字段,保证全部数据都能查询到!

2,唯一主键:如果使用传统的自增等方式,多库中的主键id势必重复,所以需要对唯一性加以控制!

解决方法:UUID(根据机器ID,时间等),redis(单线程保证不重复),snowflake算法!

3,分布式事务:

1,TCC:try控制业务代码流程,Confirm确认事务的正确性,cancel取消失败的事务!

2,基于消息系统的一致性方案:单节点事务完成后,通过发送消息保证事务提交,如果失败可通过重试,任务补偿等方式保证数据一致性!




版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:分布式事务处理及分布式事务处理的特性包括哪些
下一篇:分布式事务一致性及分布式事务一致性解决方案
相关文章