详谈分布式事务

简介: 详谈分布式事务

前言

本文是前面一篇文章聊了基于sharding的分库分表后拓展出来的关于分布式事务的讨论,本文中将详细讲一下sharding中的分布式事务以及市面上常见的分布式事务解决方案。

1.sharding的分布式事务

Sharding JDBC: Sharding JDBC 提供了对分布式事务的支持。它通过两阶段提交(2PC)协议来实现跨多个数据源的分布式事务。Sharding JDBC 会将事务中的操作发送到各个数据源执行,并在提交阶段协调所有数据源的提交操作,以保证事务的一致性。


Sharding Proxy: Sharding Proxy 本身并不直接支持分布式事务。它主要是一个中间件,负责路由和转发客户端的 SQL 请求到后端的多个数据源。在 Sharding Proxy 中,跨多个数据源的事务操作会被拆分成多个独立的本地事务,每个数据源独立处理。这就意味着在 Sharding Proxy 中,跨多个数据源的事务并不会保证全局的一致性,需要应用程序自行处理分布式事务的逻辑。


sharing jdbc后在spring boot中如何使用分布式事务,代码示例:

@Service
public class MyService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void performDistributedTransaction() {
        // 在事务范围内执行数据库操作
        jdbcTemplate.update("INSERT INTO table1 (column1) VALUES (?)", "value1");
        jdbcTemplate.update("INSERT INTO table2 (column1) VALUES (?)", "value2");
        // 手动抛出异常,模拟事务中的异常情况
        throw new RuntimeException("Simulated exception");
    }
}
 

是的你没看错,不用任何额外的配置,就是用spring的事务即可。因为sharding jdbc作为一个分库分表的技术栈,天生就有分布式事务问题,所以其默认就是开启分布式事务的,它的事务都是两阶段提交的。

说到分布式事务,我们来回顾一下之前聊过的分布式事务的内容。

2.分布式事务的产生原因

分布式事务是指在在分布式架构下一个事务中的不同子操作是在不同机器上执行的,子操作之间状态无法相互感知,其中有子操作出错后其他机器上的子操作无法正确回滚。

分布式事务产生的根本原因:各个服务之间无感知

举一个例子:

一个下单事务,包含创建订单,库存服务,需要顺序调用订单服务、库存服务。如果库存服务挂掉后,订单服务也是无法回滚的,因为订单服务感知不到库存服务是否成功,即无法感知到库存服务的状态。


3.分布式事务的解决方案

3.1.DTP模型

1994年,X/Open组织(即现在的Open Group)定义了分布式事务处理的DTP模型,该模型包括几个角色:

  • 应用程序(AP),服务
  • 事务管理器(TM),全局事务管理者
  • 资源管理器(RM),一般是数据库
  • 通信资源管理器(CRM),TM和RM之间的通信中间件

DTP模型中,一个分布式事务(全局事务)可以被拆分成多个本地事务,运行在不同的AP和和RM上。每个本地事务的ACID由自身保证,全局事务必须保证每个本地事务必须同时成功,若有一个失败其他事务必须回滚。由于本地事务之间相互是无法感知状态的,因此要通过CRM来通知各个本地事务,同步事务执行的状态。


由于各个本地事务之间需要通信,通信就需要标准,因此配套提出了XA通信标准。XA规定了DTP中CRM和TM之间的通信的接口规范,定义了用于通知事务开始、提交、终止、回滚等接口,各个数据库厂商之间必须实现该接口。

3.2.分阶段提交

分阶段提交,也叫两阶段提交(two-phase commit)。DTP模型落地实现的一种思想。

两阶段提交中,将事务分成两个阶段来执行:

  • 阶段一,准备阶段,各个本地事务执行自己事务内的逻辑,完成提交前的准备工作。
  • 阶段二,执行阶段,各个事务根据上一阶段的执行结果,进行提交或者回滚。

两阶段提交中有两个角色,协调者(coordinator)、参与者(voter)。

正常情况:

异常情况:

准备阶段协调者会询问每个参与者是否可以执行事务,每个事务参与者执行事务写入redo、undo日志,然后反馈事务的执行结果,只要有一个参与者返回的是disagree则说明失败,协调者会像各个参与者发出abort指令,各个事务收到指令后各自回滚事务。


两阶段提交的优点:

  • 方案成熟,很稳
  • 能保证强一致性

两阶段提交的缺点:

  • 单点故障,当协调者挂了之后后续的步骤就没办法进行了,被锁住的数据没办法解锁,会造成阻塞。
  • 数据锁定,分阶段提交由于过程被拆成两段,会造成本地事务的执行过程变长,数据会长时间处于锁定状态。

3.3.TCC模式

TCC模式是专门用来解决分阶段提交的缺陷的,减少数据锁定,避免阻塞问题。

TCC相当于是强化了两阶段提交,在分阶段提交中埋入了以下几个点位:

  • try,资源的检测和预留。
  • confirm,执行的业务操作提交,要求try成功的话confrim一定要能成功。
  • cancel,预留资源释放

经过TCC的强化埋点后两阶段提交变成了:

  • 准备阶段。try,资源预留,通知每个参与者去try一下数据资源,判断一下数据资源是否足以支撑之后的事务运行。每个参与者均try成功再执行下一步,否则直接cancel。
  • 执行阶段,confrim,让协调者通知各个参与者执行并提交事务(因为资源已经准备好了,执行和提交可以一气呵成),各个参与者confrim成功则成功,但凡有一个失败则通知协调者组织全局回滚、释放资源、退出。

TCC的优点:

TCC其实每个阶段都是单独的事务,try其实是个事务只是去摸一下看下阶段要的数据资源是否能用,confrim则是原来的包含业务逻辑的事务。这样的话就避免了两阶段提交中agree过程中对于数据的锁定,尽量避免了影响其他事务的执行。

TCC的缺点:

  • 编码成本,存在代码侵入需要开发人员手动编写TCC三步,开发的成本会比较高。
  • 弱一致性,TCC保证的是最终一致性,因为单个本地事务的执行和提交都包含在confirm一个步骤中,如果出现问题回滚前其实是没有保证一致性的,在这中间有其他读操作进来是能读到已经变动的数据的。

3.4.可靠消息服务


可靠消息服务起源自eBay,是将各个服务的本地事务串成一个链,依托MQ来保障分布式事务,比如服务A执行完服务后向MQ中存放自己执行成功的信息,MQ再向服务B推送消息叫它执行本地事务,服务B执行完本地事务后又告诉MQ,MQ继续向后续要执行本地事务的服务推送消息。

缺点:

由于是MQ主动向后续服务推送消息,后续服务要是失败了,前置服务感知不到,前置服务无法进行感知回滚。。所以可靠消息服务适用的场景有限。

3.5.AT模式

AT模式是Alibaba的seata组件开源出来的一种分布式事务解决方案,是对TCC的一种优化,解决了TCC模式中的的代码侵入、编码复杂等问题。


AT模式中,用户只需关注自己的业务SQL,seata框架会自动生成书屋的二阶段提交和回滚操作。


AT模式整个流程和TCC大致相同,不同点是在于AT模式将执行放在了一阶段:

  • 一阶段(开发者实现),正常编写业务逻辑,然后执行SQL,执行本地事务,并返回执行结果。
  • 二阶段(框架托管),根据一阶段的结果判断二阶段的做法,提交或者回滚。

AT模式的底层原理:

在阶段一的时候拦截下所有SQL,框架会去解析这条SQL然后去数据库中查询出要操作的数据,存一份镜像叫before image,接下来执行SQL,执行完SQL后再查询一次,存一份叫after image。


到了阶段二的时候,比对before镜像和after镜像从而判断操作是否成功,如果一阶段的所有本地事务操作都是成功的,就会清空before镜像和after镜像,如果有一个是失败的各个本地事务就会根据属于自己的before镜像进行回滚。


after镜像是为了在回滚的时候进行比对,要是回滚时发现数据库中此时的数据和after镜像中记录的数据不相同,则说明数据又被动过了,产生了脏数据,此时就需要人工介入了。由于行锁的存在,产生脏数据的概率很低很低,after镜像只是留了一手。

AT模式中的角色:

AT模式中一共有三个角色

  • TC,服务协调者,是一个单独的服务。
  • TM,通信中间件。
  • RM,资源管理器,管理分支的事务处理,与TC进行通信注册分支事务和报告事务状态,驱动分支事务提交或者回滚。

TM、RM和服务是耦合在一起的,但是不侵入代码,以jar包的方式体现。

3.6.Seata


Seata是目前为止常用、流行切稳定的分布式事务解决方案,其在使用上对代码没有侵入,直接是基于配置的,使用方法见官方手册即可。在遇到分布式事务场景时,可以优先考虑使用seata来解决。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
存储 缓存 NoSQL
详谈分布式系统缓存的设计细节
在分布式Web程序设计中,解决高并发以及内部解耦的关键技术离不开缓存和队列,而缓存角色类似计算机硬件中CPU的各级缓存。如今的业务规模稍大的互联网项目,即使在最初beta版的开发上,都会进行预留设计。但是在诸多应用场景里,也带来了某些高成本的技术问题,需要细致权衡。
1505 0
|
1月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
68 2
基于Redis的高可用分布式锁——RedLock
|
1月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
2月前
|
存储 缓存 NoSQL
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
redis分布式锁、redisson、可重入、主从一致性、WatchDog、Redlock红锁、zookeeper;Redis集群、主从复制,全量同步、增量同步;哨兵,分片集群,Redis为什么这么快,I/O多路复用模型——用户空间和内核空间、阻塞IO、非阻塞IO、IO多路复用,Redis网络模型
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
|
2月前
|
NoSQL Java Redis
分布式锁实现原理问题之使用Redis的setNx命令来实现分布式锁问题如何解决
分布式锁实现原理问题之使用Redis的setNx命令来实现分布式锁问题如何解决
|
15天前
|
存储 NoSQL Redis
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
Redis持久化、RDB和AOF方案、Redis主从集群、哨兵、分片集群、散列插槽、自动手动故障转移
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
|
1月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
这篇文章介绍了如何在SpringBoot项目中整合Redis,并探讨了缓存穿透、缓存雪崩和缓存击穿的问题以及解决方法。文章还提供了解决缓存击穿问题的加锁示例代码,包括存在问题和问题解决后的版本,并指出了本地锁在分布式情况下的局限性,引出了分布式锁的概念。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
|
1月前
|
NoSQL 安全 Java
nicelock--一个注解即可使用Redis分布式锁!
Nicelock的引入为分布式系统中的资源同步访问提供了一个简单高效和可靠的解决方案。通过注解的方式,简化了锁的实现和使用,使开发人员可以将更多精力专注于业务逻辑的实现,而不是锁的管理。此外,Nicelock在保持简单易用的同时,也提供了足够的灵活性和可靠性,满足了不同应用场景下对分布式锁的需求。
32 1
|
2月前
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
1月前
|
缓存 NoSQL 关系型数据库
(八)漫谈分布式之缓存篇:唠唠老生常谈的MySQL与Redis数据一致性问题!
本文来聊一个跟实际工作挂钩的老生常谈的问题:分布式系统中的缓存一致性。
113 10

热门文章

最新文章