聊聊分布式事务

2022-05-06 14:09:26 浏览数 (1)

愿打开此篇对你有所帮助。

好久没有写这个系列了哈。

前面几篇《打开我的收藏夹》文都让我受益匪浅,在此我还是想说一句:都是自己当初收藏的,就不要吃灰了,逐渐放空掉自己的收藏夹未必不是一件好事。

CAP 理论

C:强一致性,所有子节点数据时刻保持一致 A:整体可用 P:分区容错性,允许部分失败

至少要保证 P 吧,CA 不一定兼得的。

BASE 理论

basically available,基本可用。 优先满足 AP,以最终一致性取代强一致性。

我寻思着这俩不是集群吗?

关于缓存那些的可以翻我的redis专栏,一致性哈希我要再写一篇。

题外话

我一直追崇一个技术点不仅要理解其原理,还需要知道这个技术点解决了哪些痛点,这些痛点又是由什么引发的?这个技术还未诞生的时候是如何解决的? 也就是整体的演进过程,历史脉络。 比如为何需要HTTP?HTTP0.9为何需要演进到HTTP1.0? 进而又向1.1、2演进到最新要将Google开发的基于UDP的QUIC协议应用到HTTP3中。 搞懂这些来龙去脉相信你不仅仅对HTTP会有更深层次的理解,对网络也会有更加深刻的认识。当然也不是说一样东西一来就得全盘理清,有些东西还是比较复杂的,只是说我们要往这个方向去学。 这也是我一直觉得很重要的一个学习思想,知其然知其所以然,会让大家更好的理解很多东西。

这里的“我”指敖丙,我觉得他这段话非常之有道理,以后我要把这种做法贯彻到底!

分布式事务

(本分支图片来源于:面试官问我知道的分布式事务,我一口气说了六种)

2PC

二阶段提交,一种强一致性设计:

一荣俱荣,一损俱损。

第一阶段的协调者有超时机制,假设因为网络原因没有收到某参与者的响应或某参与者挂了,那么超时后就会判断事务失败,向所有参与者发送回滚命令。

眼尖的小伙伴多看两眼,就能看出一个问题来。我先不公布,拿张图挡一下,要自己想的就先别划下去。

有没有想过,协调者故障,或者事务失败了呢?

协调者压力可是很大的哦,因为它要为身后那些数据库撑起一片天哈。

如果协调者事务失败,这里有两种情况。

第一种是第二阶段执行的是回滚事务操作,那么答案是不断重试,直到所有参与者都回滚了,不然那些在第一阶段准备成功的参与者会一直阻塞着。

第二种是第二阶段执行的是提交事务操作,那么答案也是不断重试,因为有可能一些参与者的事务已经提交成功了,到最后真的不行只能人工介入处理。

协调者单点故障分析
代码语言:javascript复制
Switch 协调者挂的时间:
	case 发布准备命令前{
		挂吧,事务还没开始,去修复它
	};
	case 发布准备命令后{
		锁定了资源的事务参与者将可能陷入阻塞,因为资源被锁定了嘛
	}
	case 发送回滚事务命令前{
		同上
	}
	case 发送回滚事务命令后{
		很大概率没事儿,但是出现网络分区就有可能会有些参与者收不到命令阻塞
	}
	case 发送提交事务命令前{
		好了,全死了
	}
	case 发送提交事务命令后{
		同上上
	}
	default{
	}
}
协调者单点故障解决方案

了解一下主从嘛。通过选举赶紧的再来一个协调者顶上去。

还是对上面进行一个划分处理哈:

代码语言:javascript复制
Switch 协调者挂的时间:
	case 发布准备命令前{
		没事,小意思
	};
	case 发布准备命令后{
		每个参与者自身的状态只有自己和协调者知道
		这时候就需要日志了,不然新协调者哪里知道谁OK不OK啊
	}
	case 发送回滚事务命令前{
		同上
	}
	case 发送回滚事务命令后{
		同上
	}
	case 发送提交事务命令前{
		这个好办,统统滚回去吧
	}
	case 发送提交事务命令后{
		同上上
	}
	default{
	}
}

2PC 是一种尽量保证强一致性的分布式事务,因此它是同步阻塞的,而同步阻塞就导致长久的资源锁定问题,总体而言效率低,并且存在单点故障问题,在极端条件下存在数据不一致的风险。

但是也不能说人家就没用是吧,那不是还有个场景必须要强一致性嘛,那个ACID的那个。

数据库

3PC

相比于 2PC 增加了预提交阶段。

相比于 2PC 有什么升级?

1、准备阶段的变更成不会直接执行事务,而是会先去询问此时的参与者是否有条件接这个事务。因此性能会差一些,而且绝大部分的情况下资源应该都是可用的。 2、预提交阶段的引入起到了一个统一状态的作用,如果是等待预提交命令超时,那该干啥就干啥了,反正本来啥也没干。

3PC 通过预提交阶段可以减少故障恢复时候的复杂性,但是不能保证数据一致,除非挂了的那个参与者恢复。

TCC

2PC 和 3PC 都是数据库层面的,而 TCC(Try - Confirm - Cancel) 是业务层面的分布式事务。

TCC模型有个事务管理者的角色,用来记录TCC全局事务状态并提交或者回滚事务。

相对于 2PC、3PC ,TCC 适用的范围更大,但是开发量也更大,毕竟都在业务上实现,而且有时候你会发现这三个方法还真不好写。不过也因为是在业务上实现的,所以TCC可以跨数据库、跨不同的业务系统来实现事务。

本地消息表

本地消息表其实就是利用了 各系统本地的事务来实现分布式事务。 本地消息表顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中,然后在执行业务的时候 将业务的执行和将消息放入消息表中的操作放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。 然后再去调用下一个操作,如果下一个操作调用成功了好说,消息表的消息状态可以直接改成已成功。 如果调用失败也没事,会有 后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。 这时候有可能消息对应的操作不成功,因此也需要重试,重试就得保证对应服务的方法是幂等的,而且一般重试会有最大次数,超过最大次数可以记录下报警让人工处理。 可以看到本地消息表其实实现的是最终一致性,容忍了数据暂时不一致的情况。

消息队列

就是消息队列嘛,这个概念还不熟悉嘛?

半消息不是说一半消息,而是这个消息对消费者来说不可见。

最大努力通知

举个很常见的例子,我给你发了个QQ消息,你一直不去点,你一打开QQ就可以看到我有条消息等着你去处理,这既是最大努力通知。 或者,想一下 epoll 的 LT 模式。

哎,八篇到这儿就捋出这点来,不知道是我菜还是当时眼光不太好。

0 人点赞