本篇主要内容以下:java
前言
咱们都在讨论分布式,特别是面试的时候,无论是招初级软件工程师仍是高级,都会要求懂分布式,甚至要求用过。传得沸沸扬扬的分布式究竟是什么东东,有什么优点?程序员
借用火影忍术
看过火影
的同窗确定知道漩涡鸣人
的招牌忍术:多重影分身之术
。面试
- 这个术有一个特别厉害的地方,
过程和心得
:多个分身的感觉和经历都是相通的。好比 A 分身去找卡卡西(鸣人的老师)请教问题,那么其余分身也会知道 A 分身问的什么问题。 漩涡鸣人
有另一个超级厉害的忍术,须要由几个影分身完成:风遁·螺旋手里剑
。这个忍术是靠三个鸣人一块儿协做完成的。
这两个忍术和分布式有什么关系?算法
-
分布在不一样地方的系统或服务,是彼此相互关联的。数据库
-
分布式系统是分工合做的。编程
案例:小程序
- 好比 Redis 的
哨兵机制
,能够知道集群环境下哪台Redis
节点挂了。 - Kafka的
Leader 选举机制
,若是某个节点挂了,会从follower
中从新选举一个 leader 出来。(leader 做为写数据的入口,follower 做为读的入口)
那多重影分身之术
有什么缺点?缓存
- 会消耗大量的查克拉。分布式系统一样具备这个问题,须要几倍的资源来支持。
对分布式的通俗理解
- 是一种工做方式
- 若干独立计算机的集合,这些计算机对于用户来讲就像单个相关系统
- 将不一样的业务分布在不一样的地方
优点能够从两方面考虑:一个是宏观,一个是微观。
- 宏观层面:多个功能模块糅合在一块儿的系统进行服务拆分,来解耦服务间的调用。
- 微观层面:将模块提供的服务分布到不一样的机器或容器里,来扩大服务力度。
任何事物有阴必有阳,那分布式又会带来哪些问题呢?
- 须要更多优质人才懂分布式,人力成本增长
- 架构设计变得异常复杂,学习成本高
- 运维部署和维护成本显著增长
- 多服务间链路变长,开发排查问题难度加大
- 环境高可靠性问题
- 数据幂等性问题
- 数据的顺序问题
- 等等
讲到分布式
不得不知道 CAP
定理和 Base
理论,这里给不知道的同窗作一个扫盲。安全
CAP 定理
在理论计算机科学中,CAP 定理指出对于一个分布式计算系统来讲,不可能通是知足如下三点:微信
- 一致性(Consistency)
- 全部节点访问同一份最新的数据副本。
- 可用性(Availability)
- 每次请求都能获取到非错的响应,但不保证获取的数据为最新数据
- 分区容错性(Partition tolerance)
- 不能在时限内达成数据一致性,就意味着发生了分区的状况,必须就当前操做在 C 和 A 之间作出选择)
BASE 理论
BASE
是 Basically Available
(基本可用)、Soft state
(软状态)和 Eventually consistent
(最终一致性)三个短语的缩写。BASE
理论是对 CAP
中 AP
的一个扩展,经过牺牲强一致性来得到可用性,当出现故障容许部分不可用但要保证核心功能可用,容许数据在一段时间内是不一致的,但最终达到一致状态。知足 BASE
理论的事务,咱们称之为柔性事务
。
- 基本可用 : 分布式系统在出现故障时,容许损失部分可用功能,保证核心功能可用。如电商网址交易付款出现问题来,商品依然能够正常浏览。
- 软状态: 因为不要求强一致性,因此BASE容许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,如订单中的“支付中”、“数据同步中”等状态,待数据最终一致后状态改成“成功”状态。
- 最终一致性: 最终一致是指的通过一段时间后,全部节点数据都将会达到一致。如订单的“支付中”状态,最终会变为“支付成功”或者“支付失败”,使订单状态与实际交易结果达成一致,但须要必定时间的延迟、等待。
1、分布式消息队列的坑
消息队列如何作分布式?
将消息队列里面的消息分摊到多个节点(指某台机器或容器)上,全部节点的消息队列之和就包含了全部消息。
1. 消息队列的坑之非幂等
(1)幂等性概念
所谓幂等性就是不管多少次操做和第一次的操做结果同样。若是消息被屡次消费,颇有可能形成数据的不一致。而若是消息不可避免地被消费屡次,若是咱们开发人员能经过技术手段保证数据的先后一致性,那也是能够接受的,这让我想起了 Java 并发编程中的 ABA 问题,若是出现了 [ABA 问题](用积木讲解 ABA 原理 | 老婆竟然又听懂了!),若能保证全部数据的先后一致性也能接受。
(2)场景分析
RabbitMQ
、RocketMQ
、Kafka
消息队列中间件都有可能出现消息重复消费问题。这种问题并非 MQ 本身保证的,而是须要开发人员来保证。
这几款消息队列中间都是是全球最牛的分布式消息队列,那确定考虑到了消息的幂等性。咱们以 Kafka 为例,看看 Kafka 是怎么保证消息队列的幂等性。
Kafka 有一个 偏移量
的概念,表明着消息的序号,每条消息写到消息队列都会有一个偏移量,消费者消费了数据以后,每过一段固定的时间,就会把消费过的消息的偏移量提交一下,表示已经消费过了,下次消费就从偏移量后面开始消费。
> 坑:
当消费完消息后,还没来得及提交偏移量,系统就被关机了,那么未提交偏移量的消息则会再次被消费。
以下图所示,队列中的数据 A、B、C,对应的偏移量分别为 100、10一、102,都被消费者消费了,可是只有数据 A 的偏移量 100 提交成功,另外 2 个偏移量因系统重启而致使未及时提交。
重启后,消费者又是拿偏移量 100 之后的数据,从偏移量 101 开始拿消息。因此数据 B 和数据 C 被重复消息。
以下图所示:
(3)避坑指南
- 微信支付结果通知场景
- 微信官方文档上提到微信支付通知结果可能会推送屡次,须要开发者自行保证幂等性。第一次咱们能够直接修改订单状态(如支付中 -> 支付成功),第二次就根据订单状态来判断,若是不是支付中,则不进行订单处理逻辑。
- 插入数据库场景
- 每次插入数据时,先检查下数据库中是否有这条数据的主键 id,若是有,则进行更新操做。
- 写 Redis 场景
- Redis 的
Set
操做自然幂等性,因此不用考虑 Redis 写数据的问题。
- Redis 的
- 其余场景方案
- 生产者发送每条数据时,增长一个全局惟一 id,相似订单 id。每次消费时,先去 Redis 查下是否有这个 id,若是没有,则进行正常处理消息,且将 id 存到 Redis。若是查到有这个 id,说明以前消费过,则不要进行重复处理这条消息。
- 不一样业务场景,可能会有不一样的幂等性方案,你们选择合适的便可,上面的几种方案只是提供常见的解决思路。
2. 消息队列的坑之消息丢失
> 坑:
消息丢失会带来什么问题?若是是订单下单、支付结果通知、扣费相关的消息丢失,则可能形成财务损失,若是量很大,就会给甲方带来巨大损失。
那消息队列是否能保证消息不丢失呢?答案:否。主要有三种场景会致使消息丢失。
(1)生产者存放消息的过程当中丢失消息
解决方案
- 事务机制(不推荐,异步方式)
对于 RabbitMQ 来讲,生产者发送数据以前开启 RabbitMQ 的事务机制channel.txselect
,若是消息没有进队列,则生产者受到异常报错,并进行回滚 channel.txRollback
,而后重试发送消息;若是收到了消息,则能够提交事务 channel.txCommit
。但这是一个同步的操做,会影响性能。
- confirm 机制(推荐,异步方式)
咱们能够采用另一种模式: confirm
模式来解决同步机制的性能问题。每次生产者发送的消息都会分配一个惟一的 id,若是写入到了 RabbitMQ 队列中,则 RabbitMQ 会回传一个 ack
消息,说明这个消息接收成功。若是 RabbitMQ 没能处理这个消息,则回调 nack
接口。说明须要重试发送消息。
也能够自定义超时时间 + 消息 id 来实现超时等待后重试机制。但可能出现的问题是调用 ack 接口时失败了,因此会出现消息被发送两次的问题,这个时候就须要保证消费者消费消息的幂等性。
事务模式
和 confirm
模式的区别:
- 事务机制是同步的,提交事务后悔被阻塞直到提交事务完成后。
- confirm 模式异步接收通知,但可能接收不到通知。须要考虑接收不到通知的场景。
(2)消息队列丢失消息
消息队列的消息能够放到内存中,或将内存中的消息转到硬盘(好比数据库)中,通常都是内存和硬盘中都存有消息。若是只是放在内存中,那么当机器重启了,消息就所有丢失了。若是是硬盘中,则可能存在一种极端状况,就是将内存中的数据转换到硬盘的期间中,消息队列出问题了,未能将消息持久化到硬盘。
解决方案
- 建立
Queue
的时候将其设置为持久化。这个地方没搞懂,欢迎探讨解答。 - 发送消息的时候将消息的
deliveryMode
设置为 2 。 - 开启生产者
confirm
模式,能够重试发送消息。
(3)消费者丢失消息
消费者刚拿到数据,还没开始处理消息,结果进程由于异常退出了,消费者没有机会再次拿到消息。
解决方案
- 关闭 RabbitMQ 的自动
ack
,每次生产者将消息写入消息队列后,就自动回传一个ack
给生产者。 - 消费者处理完消息再主动
ack
,告诉消息队列我处理完了。
问题: 那这种主动 ack
有什么漏洞了?若是 主动 ack
的时候挂了,怎么办?
则可能会被再次消费,这个时候就须要幂等处理了。
问题: 若是这条消息一直被重复消费怎么办?
则须要有加上重试次数的监测,若是超过必定次数则将消息丢失,记录到异常表或发送异常通知给值班人员。
(4)RabbitMQ 消息丢失总结
(5)Kafka 消息丢失
场景:Kafka
的某个 broker(节点)宕机了,从新选举 leader (写入的节点)。若是 leader 挂了,follower 还有些数据未同步完,则 follower 成为 leader 后,消息队列会丢失一部分数据。
解决方案
- 给 topic 设置
replication.factor
参数,值必须大于 1,要求每一个 partition 必须有至少 2 个副本。 - 给 kafka 服务端设置
min.insyc.replicas
必须大于 1,表示一个 leader 至少一个 follower 还跟本身保持联系。
3. 消息队列的坑之消息乱序
> 坑:
用户先下单成功,而后取消订单,若是顺序颠倒,则最后数据库里面会有一条下单成功的订单。
RabbitMQ 场景:
- 生产者向消息队列按照顺序发送了 2 条消息,消息1:增长数据 A,消息2:删除数据 A。
- 指望结果:数据 A 被删除。
- 可是若是有两个消费者,消费顺序是:消息二、消息 1。则最后结果是增长了数据 A。
RabbitMQ 解决方案:
- 将 Queue 进行拆分,建立多个内存 Queue,消息 1 和 消息 2 进入同一个 Queue。
- 建立多个消费者,每个消费者对应一个 Queue。
Kafka 场景:
- 建立了 topic,有 3 个 partition。
- 建立一条订单记录,订单 id 做为 key,订单相关的消息都丢到同一个 partition 中,同一个生产者建立的消息,顺序是正确的。
- 为了快速消费消息,会建立多个消费者去处理消息,而为了提升效率,每一个消费者可能会建立多个线程来并行的去拿消息及处理消息,处理消息的顺序可能就乱序了。
Kafka 解决方案:
- 解决方案和 RabbitMQ 相似,利用多个 内存 Queue,每一个线程消费 1个 Queue。
- 具备相同 key 的消息 进同一个 Queue。
4. 消息队列的坑之消息积压
消息积压:消息队列里面有不少消息来不及消费。
场景 1: 消费端出了问题,好比消费者都挂了,没有消费者来消费了,致使消息在队列里面不断积压。
场景 2: 消费端出了问题,好比消费者消费的速度太慢了,致使消息不断积压。
> 坑:好比线上正在作订单活动,下单所有走消息队列,若是消息不断积压,订单都没有下单成功,那么将会损失不少交易。
解决方案:解铃还须系铃人
- 修复代码层面消费者的问题,确保后续消费速度恢复或尽量加快消费的速度。
- 停掉现有的消费者。
- 临时创建好原先 5 倍的 Queue 数量。
- 临时创建好原先 5 倍数量的 消费者。
- 将堆积的消息所有转入临时的 Queue,消费者来消费这些 Queue。
5. 消息队列的坑之消息过时失效
> 坑:
RabbitMQ 能够设置过时时间,若是消息超过必定的时间尚未被消费,则会被 RabbitMQ 给清理掉。消息就丢失了。
解决方案:
- 准备好批量重导的程序
- 手动将消息闲时批量重导
6. 消息队列的坑之队列写满
> 坑:
当消息队列因消息积压致使的队列快写满,因此不能接收更多的消息了。生产者生产的消息将会被丢弃。
解决方案:
- 判断哪些是无用的消息,RabbitMQ 能够进行
Purge Message
操做。 - 若是是有用的消息,则须要将消息快速消费,将消息里面的内容转存到数据库。
- 准备好程序将转存在数据库中的消息再次重导到消息队列。
- 闲时重导消息到消息队列。
2、分布式缓存的坑
在高频访问数据库的场景中,咱们会在业务层和数据层之间加入一套缓存机制,来分担数据库的访问压力,毕竟访问磁盘 I/O 的速度是很慢的。好比利用缓存来查数据,可能5ms就能搞定,而去查数据库可能须要 50 ms,差了一个数量级。而在高并发的状况下,数据库还有可能对数据进行加锁,致使访问数据库的速度更慢。
分布式缓存咱们用的最多的就是 Redis了,它能够提供分布式缓存服务。
1. Redis 数据丢失的坑
哨兵机制
Redis 能够实现利用哨兵机制
实现集群的高可用。那什么十哨兵机制呢?
- 英文名:
sentinel
,中文名:哨兵
。 - 集群监控:负责主副进程的正常工做。
- 消息通知:负责将故障信息报警给运维人员。
- 故障转移:负责将主节点转移到备用节点上。
- 配置中心:通知客户端更新主节点地址。
- 分布式:有多个哨兵分布在每一个主备节点上,互相协同工做。
- 分布式选举:须要大部分哨兵都赞成,才能进行主备切换。
- 高可用:即便部分哨兵节点宕机了,哨兵集群仍是能正常工做。
> 坑:
当主节点发生故障时,须要进行主备切换,可能会致使数据丢失。
异步复制数据致使的数据丢失
主节点异步同步数据给备用节点的过程当中,主节点宕机了,致使有部分数据未同步到备用节点。而这个从节点又被选举为主节点,这个时候就有部分数据丢失了。
脑裂致使的数据丢失
主节点所在机器脱离了集群网络,实际上自身仍是运行着的。但哨兵选举出了备用节点做为主节点,这个时候就有两个主节点都在运行,至关于两个大脑在指挥这个集群干活,但到底听谁的呢?这个就是脑裂。
那怎么脑裂怎么会致使数据丢失呢?若是发生脑裂后,客户端还没来得及切换到新的主节点,连的仍是第一个主节点,那么有些数据仍是写入到了第一个主节点里面,新的主节点没有这些数据。那等到第一个主节点恢复后,会被做为备用节点连到集群环境,并且自身数据会被清空,从新重新的主节点复制数据。而新的主节点因没有客户端以前写入的数据,因此致使数据丢失了一部分。
避坑指南
- 配置 min-slaves-to-write 1,表示至少有一个备用节点。
- 配置 min-slaves-max-lag 10,表示数据复制和同步的延迟不能超过 10 秒。最多丢失 10 秒的数据
注意:缓存雪崩
、缓存穿透
、缓存击穿
并非分布式所独有的,单机的时候也会出现。因此不在分布式的坑之列。
3、分库分表的坑
1.分库分表的坑之扩容
分库、分表、垂直拆分和水平拆分
-
分库: 因一个数据库支持的最高并发访问数是有限的,能够将一个数据库的数据拆分到多个库中,来增长最高并发访问数。
-
分表: 因一张表的数据量太大,用索引来查询数据都搞不定了,因此能够将一张表的数据拆分到多张表,查询时,只用查拆分后的某一张表,SQL 语句的查询性能获得提高。
-
分库分表优点:分库分表后,承受的并发增长了多倍;磁盘使用率大大下降;单表数据量减小,SQL 执行效率明显提高。
-
水平拆分: 把一个表的数据拆分到多个数据库,每一个数据库中的表结构不变。用多个库抗更高的并发。好比订单表每月有500万条数据累计,每月均可以进行水平拆分,将上个月的数据放到另一个数据库。
-
垂直拆分: 把一个有不少字段的表,拆分红多张表到同一个库或多个库上面。高频访问字段放到一张表,低频访问的字段放到另一张表。利用数据库缓存来缓存高频访问的行数据。好比将一张不少字段的订单表拆分红几张表分别存不一样的字段(能够有冗余字段)。
-
分库、分表的方式:
- 根据租户来分库、分表。
- 利用时间范围来分库、分表。
- 利用 ID 取模来分库、分表。
> 坑:
分库分表是一个运维层面须要作的事情,有时会采起凌晨宕机开始升级。可能熬夜到天亮,结果升级失败,则须要回滚,其实对技术团队都是一种煎熬。
怎么作成自动的来节省分库分表的时间?
- 双写迁移方案:迁移时,新数据的增删改操做在新库和老库都作一遍。
- 使用分库分表工具 Sharding-jdbc 来完成分库分表的累活。
- 使用程序来对比两个库的数据是否一致,直到数据一致。
> 坑:
分库分表看似光鲜亮丽,但分库分表会引入什么新的问题呢?
垂直拆分带来的问题
- 依然存在单表数据量过大的问题。
- 部分表没法关联查询,只能经过接口聚合方式解决,提高了开发的复杂度。
- 分布式事处理复杂。
水平拆分带来的问题
- 跨库的关联查询性能差。
- 数据屡次扩容和维护量大。
- 跨分片的事务一致性难以保证。
2.分库分表的坑之惟一 ID
为何分库分表须要惟一 ID
- 若是要作分库分表,则必须得考虑表主键 ID 是全局惟一的,好比有一张订单表,被分到 A 库和 B 库。若是 两张订单表都是从 1 开始递增,那查询订单数据时就错乱了,不少订单 ID 都是重复的,而这些订单其实不是同一个订单。
- 分库的一个指望结果就是将访问数据的次数分摊到其余库,有些场景是须要均匀分摊的,那么数据插入到多个数据库的时候就须要交替生成惟一的 ID 来保证请求均匀分摊到全部数据库。
> 坑:
惟一 ID 的生成方式有 n 种,各有各的用途,别用错了。
生成惟一 ID 的原则
- 全局惟一性
- 趋势递增
- 单调递增
- 信息安全
生成惟一 ID 的几种方式
-
数据库自增 ID。每一个数据库每增长一条记录,本身的 ID 自增 1。
- 缺点
- 多个库的 ID 可能重复,这个方案能够直接否掉了,不适合分库分表后的 ID 生成。
- 信息不安全
- 缺点
-
适用
UUID
惟一 ID。- 缺点
- UUID 太长、占用空间大。
- 不具备有序性,做为主键时,在写入数据时,不能产生有顺序的 append 操做,只能进行 insert 操做,致使读取整个
B+
树节点到内存,插入记录后将整个节点写回磁盘,当记录占用空间很大的时候,性能不好。
- 缺点
-
获取系统当前时间做为惟一 ID。
- 缺点
- 高并发时,1 ms内可能有多个相同的 ID。
- 信息不安全
- 缺点
-
Twitter 的
snowflake
(雪花算法):Twitter 开源的分布式 id 生成算法,64 位的 long 型的 id,分为 4 部分-
1 bit:不用,统一为 0
-
41 bits:毫秒时间戳,能够表示 69 年的时间。
-
10 bits:5 bits 表明机房 id,5 个 bits 表明机器 id。最多表明 32 个机房,每一个机房最多表明 32 台机器。
-
12 bits:同一毫秒内的 id,最多 4096 个不一样 id,自增模式
-
优势:
-
毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
-
不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是很是高的。
-
能够根据自身业务特性分配bit位,很是灵活。
-
-
缺点:
- 强依赖机器时钟,若是机器上时钟回拨(能够搜索 2017 年闰秒 7:59:60),会致使发号重复或者服务会处于不可用状态。
-
-
百度的
UIDGenerator
算法。- 基于 Snowflake 的优化算法。
- 借用将来时间和双 Buffer 来解决时间回拨与生成性能等问题,同时结合 MySQL 进行 ID 分配。
-
美团的
Leaf-Snowflake
算法。- 为何叫 Leaf(叶子):来自数学家莱布尼茨的一句话:“世界上没有两片相同的树叶”,也就是说这个算法生成的 ID 是惟一的。
- 获取 id 是经过代理服务访问数据库获取一批 id(号段)。
- 双缓冲:当前一批的 id 使用 10%时,再访问数据库获取新的一批 id 缓存起来,等上批的 id 用完后直接用。
- 优势:
- Leaf服务能够很方便的线性扩展,性能彻底可以支撑大多数业务场景。
- ID号码是趋势递增的8byte的64位数字,知足上述数据库存储的主键要求。
- 容灾性高:Leaf服务内部有号段缓存,即便DB宕机,短期内Leaf仍能正常对外提供服务。
- 能够自定义max_id的大小,很是方便业务从原有的ID方式上迁移过来。
- 即便DB宕机,Leaf仍能持续发号一段时间。
- 偶尔的网络抖动不会影响下个号段的更新。
- 缺点:
- ID号码不够随机,可以泄露发号数量的信息,不太安全。
4、分布式事务的坑
怎么理解事务?
-
事务能够简单理解为要么这件事情所有作完,要么这件事情一点都没作,跟没发生同样。
-
在分布式的世界中,存在着各个服务之间相互调用,链路可能很长,若是有任何一方执行出错,则须要回滚涉及到的其余服务的相关操做。好比订单服务下单成功,而后调用营销中心发券接口发了一张代金券,可是微信支付扣款失败,则须要退回发的那张券,且须要将订单状态改成异常订单。
> 坑
:如何保证分布式中的事务正确执行,是个大难题。
分布式事务的几种主要方式
- XA 方案(两阶段提交方案)
- TCC 方案(try、confirm、cancel)
- SAGA 方案
- 可靠消息最终一致性方案
- 最大努力通知方案
XA 方案原理
- 事务管理器负责协调多个数据库的事务,先问问各个数据库准备好了吗?若是准备好了,则在数据库执行操做,若是任一数据库没有准备,则回滚事务。
- 适合单体应用,不适合微服务架构。由于每一个服务只能访问本身的数据库,不容许交叉访问其余微服务的数据库。
TCC 方案
- Try 阶段:对各个服务的资源作检测以及对资源进行锁定或者预留。
- Confirm 阶段:各个服务中执行实际的操做。
- Cancel 阶段:若是任何一个服务的业务方法执行出错,须要将以前操做成功的步骤进行回滚。
应用场景:
- 跟支付、交易打交道,必须保证资金正确的场景。
- 对于一致性要求高。
缺点:
- 但由于要写不少补偿逻辑的代码,且不易维护,因此其余场景建议不要这么作。
Sega 方案
基本原理:
- 业务流程中的每一个步骤如有一个失败了,则补偿前面操做成功的步骤。
适用场景:
- 业务流程长、业务流程多。
- 参与者包含其余公司或遗留系统服务。
优点:
- 第一个阶段提交本地事务、无锁、高性能。
- 参与者可异步执行、高吞吐。
- 补偿服务易于实现。
缺点:
- 不保证事务的隔离性。
可靠消息一致性方案
基本原理:
- 利用消息中间件
RocketMQ
来实现消息事务。 - 第一步:A 系统发送一个消息到 MQ,MQ将消息状态标记为
prepared
(预备状态,半消息),该消息没法被订阅。 - 第二步:MQ 响应 A 系统,告诉 A 系统已经接收到消息了。
- 第三步:A 系统执行本地事务。
- 第四步:若 A 系统执行本地事务成功,将
prepared
消息改成commit
(提交事务消息),B 系统就能够订阅到消息了。 - 第五步:MQ 也会定时轮询全部
prepared
的消息,回调 A 系统,让 A 系统告诉 MQ 本地事务处理得怎么样了,是继续等待仍是回滚。 - 第六步:A 系统检查本地事务的执行结果。
- 第七步:若 A 系统执行本地事务失败,则 MQ 收到
Rollback
信号,丢弃消息。若执行本地事务成功,则 MQ 收到Commit
信号。 - B 系统收到消息后,开始执行本地事务,若是执行失败,则自动不断重试直到成功。或 B 系统采起回滚的方式,同时要经过其余方式通知 A 系统也进行回滚。
- B 系统须要保证幂等性。
最大努力通知方案
基本原理:
- 系统 A 本地事务执行完以后,发送消息到 MQ。
- MQ 将消息持久化。
- 系统 B 若是执行本地事务失败,则
最大努力服务
会定时尝试从新调用系统 B,尽本身最大的努力让系统 B 重试,重试屡次后,仍是不行就只能放弃了。转到开发人员去排查以及后续人工补偿。
几种方案如何选择
- 跟支付、交易打交道,优先 TCC。
- 大型系统,但要求不那么严格,考虑 消息事务或 SAGA 方案。
- 单体应用,建议 XA 两阶段提交就能够了。
- 最大努力通知方案建议都加上,毕竟不可能一出问题就交给开发排查,先重试几回看能不能成功。
写在最后
分布式还有不少坑,这篇只是一个小小的总结,从这些坑中,咱们也知道分布式有它的优点也有它的劣势,那到底该不应用分布式,彻底取决于业务、时间、成本以及开发团队的综合实力。后续我会继续分享分布式中的一些底层原理,固然也少不了分享一些避坑指南。
参考资料:
美团的 Leaf-Snowflake 算法。
百度的 UIDGenerator 算法。
Advanced-java
> 你好,我是悟空哥
,「7年项目开发经验,全栈工程师,开发组长,超喜欢图解编程底层原理」。
我还手写了 2 个小程序
,Java 刷题小程序
,PMP 刷题小程序
,点击个人公众号菜单打开!
另外有 111 本架构师资料以及 1000 道 Java 面试题,都整理成了PDF。
能够关注公众号 「悟空聊架构」 回复 悟空
领取优质资料。
「转发->在看->点赞->收藏->评论!!!」 是对我最大的支持!
《Java并发必知必会》系列:
1.反制面试官 | 14张原理图 | 不再怕被问 volatile!
5.5000字 | 24张图带你完全理解Java中的21种锁