文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

再谈分布式事务,你了解多少?

2024-12-02 08:53

关注

三年前,我写了第一篇和分布式事务相关的文章再有人问你分布式事务,把这篇扔给他,后面陆续也写了一些和分布式事务相关的文章:

时隔三年,回看之前的文章,之前的确也有很多漏掉的一些知识,所以今天在这里再次和大家总结下分布式事务相关的东西。

事务

首先还是先说一下事务的定义吧,事务的英语是transaction,我们查找词典可以发现这个单词的中文解释是交易,买卖等含义,所以我们可以知道事务一定和交易密不可分他们才能共享一个英文单词,而交易的定义是什么呢?有句俗话说得好,一手交钱,一手交货,那这个就是交易的规则,而这个同时也是事务的定义。那么事务的官方定义是什么呢?

事务是一系列操作的集合,这些操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的最小工作单元。

可以发现这个基本和我们一手交钱,一手交货很像,的确在现实的开发环境中,在交易的业务中对事务的保证特别看重,而一些社交类的业务,和资金关系不大的,比如点赞数,评论数,是不会对事务特别看重,这些应该关注的是性能。

事务的类型

之前的文章都没有介绍过事务的类型相关,直到之前看了一篇文章,才知道事务是分了5种类型的,这里也介绍给大家:

扁平事务

我们日常使用的基本都是扁平事务,以begin开始,然后以commit 或者 rollback结束.

begin;
do xxxx;
commit/rollback;

带保存点的扁平事务

增加了SavePoint机制,内存保存,如果数据库宕机,savepoint将会丢失。

begin

insert into xxx

savepoint a

insert into yyy

rollback to a

这里的rollback to a只会回滚到保存点a这里,不会整个事务都回滚

链式事务

当我们提交事务后,相当于执行了 COMMIT AND CHAIN,也就是开启一个链式事务,即当我们提交事务之后会开启一个相同隔离级别的事务。如果回滚只会回滚当前节点。

通过下面sql语句可以再mysql里面查到当前是否开启链式模式,以及如何开启

select @@completion_type
set @@completion_type = 1 // 0无链式,

嵌套事务

分布式事务

通常是一个在分布式环境下运行的扁平事务,需要根据数据所在位置访问网络中的不同节点,一般来说对于分布式事务,其同样需要满足单机 ACID 特性,要么都发生,要么都失效。但是实际实现的情况,可能远比理想的更为复杂,所以通常会降低要求。

单机事务

上面讲了五种类型的事务,前4种其实都可以归结为单机事务,而单机事务也是后端程序员中经常接触到的,所以先简单讲讲单机事务的核心关键点:

ACID

ACID是单机事务的四大特性,由这四个特性可以定义到底什么条件下才能算作事务。

实现关键

这里只讲一下mysql的一些实现关键:

整体来说事务的ACID是通过InnoDB日志和锁来保证:。

redolog,undolog,binlog通常会容易搞混淆,这里简单说一下

undolog:用于回滚和mvcc,可以理解他用于记录之前版本的数据。

redolog:数据库为了加快刷盘速度,采用了WAL的方式,也就是先顺序预写日志,然后再异步去刷盘,而redolog就是顺序写日志的产物。

binlog:上面都是innodb引擎的日志,binlog是mysql-server的日志,用于主从同步等作用。

代码使用

java程序员的话如果使用的是spring,那么使用本地事务会有两种手段:

声明式事务

其实就是直接加个注解,spring自己会做一个切面代理,然后使用事务,这种方法使用得应该也是最多的,因为他是最简单的,但是说如果只想控制这个方法部分代码进入事务,或者调用同一个类的方法使用事务,那就不太能使用这种方法。

编程式事务

编程式事务,实现起来稍显麻烦,需要自己手写很多代码,但是能解决上面说的那个问题。这种方式更加灵活多变。

分布式事务

为什么需要分布式事务

我们会发现分布式事务在最近几年里提到的声音越来越多,这是究竟为什么呢?在《架构即未来》这本书中提到了AKF模型是软件架构扩展的基础模型

如上图,如果我们要对一个软件进行扩展,那么需要以AKF模型为基础,从三个维度进行扩展。

在Y轴中我们之前的单体服务被拆分成了多个服务,那就有可能以前一个事务的内容,可能出现在了多个服务中,比如一个订单扣除积分和优惠券,之前是一个服务,如果拆分出来了有积分服务和优惠券服务,那么我们之前的本地事务就会失效,就需要引入分布式服务来保证一个订单中的全部扣减都是成功的。

在Z轴中,如果我们做了分库分表,也会破坏本地事务,如果大家都是一个库自然还能使用分库分表,但如果操作了不同库那么就无法保证事务,那么也需要引入分布式事务。

而AKF又是现代软件扩展的基础模型,三者有其二可能都需要引入分布式事务,所以,如果要对软件进行扩展,那么分布式事务必不可少。

分布式的理论知识

CAP和BASE的理论知识应该很多人都知道,但是我这里还是需要介绍一下,因为分布式事务离不开这两个东西。

CAP

CAP定理,又被叫作布鲁尔定理。CAP是分布式系统的入门理论。

BASE

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展。

1.基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。

2.软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。

3.最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。

BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。

分布式事务解决方案

刚性事务

刚性事务追求的是强一致性事务。

DTP/XA

DTP的XA规范(全称为Distributed Transaction Processing The XA Specification)的制定者是X/Open,即现在的Open Group。

熟悉数据库自带的分布式事务支持的同学,其实就知道这个就是XA,

这里的AP你可以理解成是我们自己的业务服务,RM则是我们使用的数据库通常都是使用mysql而mysql也自带支持XA,TM可以是一个单独的服务,也可以是我们自己的业务服务承担,他的作用是做事务管理。如果是用mysql的话,使用XA有如下代码:

XA BEGIN '123';
insert into xxx;
XA END '123'; // 链接断开会失去这次事务
XA PREPARE '123'; // 二阶段准备会持久化
XA COMMIT '123'; //prepare全部成功/或者有一个失败就回滚

当然XA其实有很多缺点:

1.数据锁定:数据在整个事务处理过程结束前,都被锁定,读写都按隔离级别的定义约束起来。

2.协议阻塞:XA prepare 后,分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞等待。

3.性能差:性能的损耗主要来自两个方面:一方面,事务协调过程,增加单个事务的 RT;另一方面,并发事务数据的锁冲突,降低吞吐。

柔性事务

因为刚性事务的实现成本较大,对于现在互联网的业务来说很多不愿意承受这么大的成本性能损失,愿意牺牲一定的一致性来保证性能,所以我们这里叫做柔性事务。

消息最终一致

适用于很多异步任务,在我们的场景中,比如异步审核笔记,异步发放积分(适用于加法的业务)。而消息最终一致也有两种实现方法。

消息表

qmq实现事务消息的方法是利用的消息表。首先我们需要有一张这样的消息表,用来和我们业务在同一个事务里面一起保存。

CREATE TABLE `msg_queue` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`topic` varchar(64) NOT NULL,
`nameServer` varchar(64) NOT NULL,
`status` smallint(6) NOT NULL DEFAULT '0' COMMENT '消息状态',
`error` int unsigned NOT NULL DEFAULT '0' COMMENT '错误次数',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='记录业务系统消息';

从上面图上可以看出,我们会有单独的task在不断扫描我们的消息表,如果消息表没有被删除掉,代表之前没有发送成功,那么我们需要做发送,这里需要说的是,我们一定要保证这个消息是幂等的,因为这里的发送完之后删除消息,并不能保证数据是一致的,有可能一个消息会发送多次,最后才能被删掉。

使用的代码如下:

@Transactional
public void pay(Order order){
saveOrder(order);
messageProducer.sendMessage(buildMessage(order)); //要写在最后
}

rocketmq事务消息

rockemq的事务消息如图上所示分为四个阶段:

最大努力通知

适合于开放平台,外部的第三方系统想要保证最终一致。比如微信,支付宝的开放平台。下面我贴一个微信支付平台的执行流程:

从上面可以看到,最大努力通知需要保证两点:

TCC

做支付的同学比较常用的,虽然是柔性事物,但是目标是有刚性的效果。隔离性比较强。

举个例子:有积分服务,券服务,余额服务,如果用户一次订单想同时扣减这三个怎么能保证。用其他的模式可以吗?

消息最终一致和最大努力通知,都不太适合,无法保证隔离型,用户重复使用资产。XA性能差。

所以这里我们选择使用了TCC,分三个方法:

SAGA

Saga是30年前一篇数据库伦理提到的一个概念。其核心思想是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。

SEATA

当然上面介绍了很多种分布式事务,有同学会想,说了这么多但是我该怎么实现呢?那我在这里推荐使用seata,seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。为用户提供了 AT、TCC、SAGA 和 XA 事务模式。

有兴趣的可以访问seata官网:https://seata.io/zh-cn 。我这里就不具体介绍了,或者看我之前的文章也有很多介绍。

最后

时隔这么久,再次写了一下关于分布式事务相关的,这次算对之前的是补充,当然也算是对分布式事务的总结。希望大家能在自己的业务中能找到合适自己的分布式事务的方法。


来源:咖啡拿铁内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯