不少时候,咱们谈概念,会感受看的时候很明白,过几天就忘光了,缘由就是缺少对应的场景。php
“
mysql
没有落地支撑的PPT都是耍流氓;sql
没有代码支撑的架构都是耍流氓;数据库
没有业务场景支撑的应用方案都是耍流氓;缓存
“服务器
----大魏语
微信
本篇咱们从实战角度分析分布式事务,因此本文会所有用大白话说清楚。网络
拷问1:为何要用分布式事务?架构
首先,若是一次请求只涉及一个数据库的表,那么不存在分布式的问题。那都叫本地事务(LT)。
app
若是一次请求涉及到两个DB的表,这时候就要用到分布式事务(DT)。主流数据库都支持事务管理。
拷问2:分布式事务的针对对象是什么?
DB?
错,是数据,data。
数据包括:DB、Cache、搜索(ES)。但不少时候,咱们在分布式事务操做最多的是DB。
拷问3:分布式事务(DT)使用什么模型?
CAP是最常被提到的模型。
CAP 理论的定义很简单,CAP 三个字母分别表明了分布式系统中三个相互矛盾的属性:
Consistency (一致性):CAP 理论中的副本一致性特指强一致性;
Availiablity(可用性):指系统在出现异常时已经能够提供服务;
Tolerance to the Partition of network (分区容忍):指系统能够对网络分区。这种异常情 况进行容错处理;
在真实的业务场景中,CAP没法同时知足,可以知足两个就很不错了。所以,仨字母组合,会有:
CA、AP、CP。在实际业务场景中,只有AP和CP。
分布式事务选择AP仍是CP,取决于业务特色,取决于业务容忍度。
网上购物场景用CP。关于这个场景,我仍是用红帽测试代码:coolstore:
若是要购物,会涉及到几个业务模块呢?大体顺序是这样的:
也就是说,咱们在电商的一次操做,会涉及:看目录、评分、购物车、减库存、下单等多个功能模块操做。不一样的功能模块由不一样的微服务支撑,有不一样的数据库。这就要保证CP,即数据一致性和分区容忍性。
咱们再看看一个AP模型的分布式事务。
大魏常常写公众号、发公众号。这里面涉及到:
新建图文消息:
写公众号内容:
而后推送给几千人:
在这个分布式事务中,其实更强调吞吐量。不少时候大魏晚上发blog后,都须要10分钟blog才能推送给全部订阅者,就是由于晚上发blog的太多,吞吐量成了瓶颈。
拷问4:分布式事务(DT)必定是异步的么?
错!
同步异步的都有,不少时候是二者混合的场景。
大魏仍是从实践角度举例。在京东买大魏的书100本,选完了下单支付,付钱,那么买书这件事用的就是同步模型。
在上图的同步模型中,设计到的模块至少有:减小商品库存、创建订单、前台支付。
若是到支付页面,一看:须要这么多钱,犹豫了,暂时先不支付了。可能过了一个小时候想清楚了,才支付,那这就变成了异步事务。咱们在京东退出支付页面时,也会提示若是24个小时未支付的话(截图的时候,已通过了几分钟),订单会自动取消。
若是咱们3个小时以后支付,确定也能支付成功。那这三个小时的时间里,事务是被保存在消息队列里了(MQ)。
因此说,分布式事务不少时候是同步和异步混合的场景。并且咱们在设计这种场景时,还须要保证业务的吞吐量和相应延迟。
因此说,分布式事务,有CP和AP两种常见模型。典型的CP如电商购物,强调一致性。典型的AP模型如发blog、发微信朋友圈。而AP和CP这两种模型,既有异步,又有同步的。
拷问5:分布式事务设计的核心要点是什么?
分布式事务设计的问题,自己是架构设计的问题。架构设计,分为:服务和数据两大块。不少时候,数据和服务是紧耦合的。所以须要进行拆分。
在微服务架构中,每一个微服务都有本身的DB,其实就是为了把分布式事务拆分红本地事务。
针对红帽coolstore的demo而言,咱们就是将长事务拆分红短事务,将短事务拆分红本地事务。
这样,每一个本地事务都只操做本身的数据,也就是说DB/缓存/MQ必需要有事务管理能力(这个后面讲),本地事务才能实现。但每一个本地事务之间是有关联的。例如:A(下订单)->B(减库存)->C(发货)。若是A成功,B成功,C成功,那么整个分布式事务成功。
那么,若是C失败呢?
C会发生回滚,B、A会发生补偿。C不须要补偿,由于本地事务能够直接rollback。
咱们看一下MySQL如何实现事务管理(BEGIN 、ROLLBACK、COMMIT):
<?php$dbhost = 'localhost:3306'; // mysql服务器主机地址$dbuser = 'root'; // mysql用户名$dbpass = '123456'; // mysql用户名密码$conn = mysqli_connect($dbhost, $dbuser, $dbpass);if(! $conn ){die('链接失败: ' . mysqli_error($conn));}// 设置编码,防止中文乱码mysqli_query($conn, "set names utf8");mysqli_select_db( $conn, 'RUNOOB' );mysqli_query($conn, "SET AUTOCOMMIT=0"); // 设置为不自动提交,由于MYSQL默认当即执行mysqli_begin_transaction($conn); // 开始事务定义
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(8)")){ mysqli_query($conn, "ROLLBACK"); // 判断当执行失败时回滚}
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(9)")){ mysqli_query($conn, "ROLLBACK"); // 判断执行失败时回滚}mysqli_commit($conn); //执行事务mysqli_close($conn);?>
而Redis事务管理的基础是:MULTI 、 EXEC 、 DISCARD 和 WATCH。
关于Redis的事务管理,能够参照这篇文章:
https://cloud.tencent.com/developer/article/1798560
拷问5:分布式事务框架不少,实际到底哪一种用的多。
谈到分布式事务的架构,有不少:XA、 TCC 、SAGA、 事务消息。咱们不谈概念,哪一种用的多呢?
实际应用中,同步主要用saga、异步主要使用事务消息。
saga就是事务补偿,这上文已经有所介绍。
MQ事务消息重点再也不是保证全部子事务的原子性,而是保证本地事务和发送MQ消息的原子性。相对于saga,MQ事务消息不用提供补偿接口)。
须要注意的是,不是全部的MQ都支持事务消息,目前只有RocketMQ支持。
如下内容节选自:https://juejin.cn/post/6844903951448408071
普通MQ的消息处理流程:
消息生成者发送消息
MQ收到消息,将消息进行持久化,在存储中新增一条记录
返回ACK给生产者
MQ push 消息给对应的消费者,而后等待消费者返回ACK
若是消息消费者在指定时间内成功返回ack,那么MQ认为消息消费成功,在存储中删除消息,即执行第6步;若是MQ在指定时间内没有收到ACK,则认为消息消费失败,会尝试从新push消息,重复执行四、五、6步骤
MQ删除消息
事务消息与普通消息的区别就在于消息生产环节,生产者首先预发送一条消息到MQ(这也被称为发送half消息)
MQ接受到消息后,先进行持久化,则存储中会新增一条状态为待发送
的消息
而后返回ACK给消息生产者,此时MQ不会触发消息推送事件
生产者预发送消息成功后,执行本地事务
执行本地事务,执行完成后,发送执行结果给MQ
MQ会根据结果删除或者更新消息状态为可发送
若是消息状态更新为可发送
,则MQ会push消息给消费者,后面消息的消费和普通消息是同样的
RocketMQ的模式有利于异步解耦。
咱们想象一个场景-秒杀系统。
如下内容节选自:https://www.jianshu.com/p/06e9a1d75bd5
流程说明以下:
注册系统向消息队列 RocketMQ 发送半事务消息。
1.1 半事务消息发送成功,进入 2。
1.2 半事务消息发送失败,注册系统不进行注册,流程结束。(最终注册系统与邮件通知系统数据一致
注册系统开始注册。
2.1 注册成功,进入 3.1。
2.2 注册失败,进行 3.2。
注册系统向消息队列 RocketMQ 发送半消息状态。
3.1 提交半事务消息,产生注册成功消息,进入 4。
3.2 回滚半事务消息,未产生注册成功消息,流程结束。(最终注册系统与邮件通知系统数据一致)
邮件通知系统接收消息队列 RocketMQ 的注册成功消息。
邮件通知系统发送注册成功邮件。(最终注册系统与邮件通知系统数据一致)
总结:
分布式事务针对的对象是data,它能够在DB/缓存/ES/MQ中.
CAP模型中,CP、AP较多。前者强调一致性,后者强调吞吐量。而AP和CP这两种模型,既有异步,又有同步的。
分布式事务设计的核心要点是:解耦拆分。将分布式事务拆成本地事务。
在分布式系统设计中:同步主要用saga、异步主要使用事务消息。事务消息不须要写补偿接口,所以代码侵入低。RocketMQ支持事务消息。