分布式系统一致性问题处理实战

作者 : 开心源码 本文共3063个字,预计阅读时间需要8分钟 发布时间: 2022-05-12 共159人阅读

一、背景及问题形容

业务背景

商户提交表单数据至旺铺(deco项目,以下皆称为deco),deco需要接入poi系统进行装修内容的人工审核,详细流程见下图。

问题

店铺装修审核状态在deco系统和poi系统之间不一致,下图中1,2,3步提交流程会出现同一次提交审核流在deco系统中的装修状态为未装修,而在poi系统为审核中。同样在4,5,6步骤的审核回调过程也会有同类的状态不一致问题。两块问题都是同一技术问题,本文只以1,2,3步提交过程为例进行分析处理。

二、问题分析

1. 关系型数据事务在分布式系统中的问题

从业务中抽离出来,问题的核心起因可使用下图流程模型来形容。

系统A的某个业务功能包含两步操作,第一步把数据写入A系统本地库,第二步远程调使用系统B,这两步操作在A系统使用一个数据库事务来包装。伪代码如下:

$db->begain();

// 第一步操作本地数据库

$res = $db->update($sql_a);

if (!$res) {

$db->rollback();

return;

}

// 第二步远程调使用B系统

$res = $http_request->get($url_b);

if (‘success’ != $res) {

$db->rollback();

return;

}

$db->commit();

第一步有两种结果成功、失败,第二步则有3种结果成功、失败、超时,其中导致超时起因可能是网络中断,也可能是B系统服务异常。那么我们根据两步骤的执行结果情况来分别分析一下能否会导致A、B系统之间的数据不一致。

可得当第一步执行成功,第二步远程调使用超时时,A系统事务会回滚,那么就发生A、B系统数据不一致的情况,A库中写入失败,但是B库中却成功写入。我们习惯于用关系型数据库事务的ACID特性来达到一致性的目的,但是当事务中发生跨系统的调使用时ACID就无效了,只从数据库层面来看,跨系统就意味着同一个业务存在多个数据副本,对应着不同的数据库实例,而且分布在不同的机器上,而关系型数据库的事务只是针对同一个库的同一个connection而言的。

2.那么怎样处理跨系统的数据一致性问题?

我们先重新认识一下什么是一致性?首先想到的是关系型数据库事务,又会想到最经典的甲给乙转钱的例子,事务的四大基本特性ACID保证了甲账户扣钱和乙账户入钱同时发生或者同时不发生,其中的C特性就是指一致性,它是指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。在web2.0之前大部分网站或者者项目都是单系统,对应底层存储也只是单数据库,所以用数据库本身的事务特性就满足了一致性。

但是随着互联网使用户和数据量的指数级增长,对于每个系统的计算能力、吞吐量以及响应速度的要求都大大提高,于是单节点服务器一定满足不了要求,所以都在考虑拆分和系统扩展性,无论是经过水平拆分还是垂直拆分,单机系统就变成了分布式系统,分布式系统就一定存在某节点或者者网络故障的情况,之前说的事务的ACID中的强一致性就无法保证。

再回到我们的问题上来,poi系统其实即可以了解为垂直拆分出的一个微服务,deco调使用poi的提交审核接口就是分布式系统之间的调使用,但是deco中依然使用关系型数据库的事务来达到强一致性的目的,根据CAP理论,分布式系统的一致性、可使用性、分区容错性不可能同时得到满足,只能满足其中两个,而分布式系统的分区容错性都需要得到满足,所以就需要在一致性和可使用性之间进行取舍。Deco的这种实现其实就是为了保证一致性,而牺牲了可使用性。

而对于现在的系统而言,可使用性是至关重要的必需要保证,要做到即便poi系统出现偶尔的网络故障或者者超时,也尽可能不要使用户的一次提交失败掉。

再来理解一个概念BASE理论,BASE理论是CAP理论的一种实现,它对分布式系统的一致性和可使用性不可兼得的问题提出了一种方案,即基本可使用和最终一致是目标。既然提到了强一致性和最终一致性,再详情一下业界对一致性的分层次定义。

强一致 :当升级操作完成之后,任何多个后续进程或者者线程的访问都会返回最新的升级过的值。这种是对使用户最友好的,就是使用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现需要牺牲可使用性。

弱一致?:系统并不保证续进程或者者线程的访问都会返回最新的升级过的值。系统在数据写入成功之后,不承诺立就可以读到最新写入的值,也不会具体的承诺多久之后可以读到。

最终一致?:弱一致性的特定形式。系统保证在没有后续升级的前提下,系统最终返回上一次升级操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信推迟,系统负载和复制副本的个数影响。DNS 是一个典型的最终一致性系统。

对上面几段分析的总结就是:关系型数据库的事务可以满足单系统的强一致性,大部分分布式系统只能把最终一致性作为追求。而我们的deco和poi系统显然也是应该追求最终一致性,由于对于poi和deco之间装修审核数据出现短时间的不一致是完全可以接受的。

三、处理方案

基于上述理论,我们可以有以下两种方案来达到可使用性和最终一致性:

方案一、

解耦,异步任务解决,由原来同步调使用poi系统变为异步调使用,将deco系统信息入库和调使用poi创立审核任务这两个操作分开。

deco系统收到使用户请求之后先信息入库,而后在本地数据库同时新建一个任务(任务初始状态为待执行),当调使用poi完成之后该任务改为已经执行,信息入库和创立任务在同一个数据库事务下。

后端定时脚原本执行待执行状态的任务。

假如异步调使用poi返回失败,则需要对之前入库的信息进行回退。

假如异步调使用poi遇到网络问题或者者超时,则考虑重试机制,注意重试机制要避免重复提交,可采取在deco系统重试前确认 或者者 在poi系统保证接口的幂等性。

方案二、

引入消息队列,相当于对方案一的更新版。

deco系统为消息生产者,poi系统为消息消费者。

生产者接收到使用户请求,业务数据解决入库,而后写入一条任务到消息队列,并且要新建一张消息状态表使用于记录消息的执行状态,以上三个操作要在同一个本地事务中进行。其实也可以在业务表添加一个字段使用来表示消息执行状态,这样有一个缺点就是生产消息和本身业务解决发生耦合,但是可以接受,由于既然放在一个事务中耦合就不可避免。

消费者取出一条消息,进行业务解决,解决完成后需要告诉Deco系统消息执行结果,成功或者者失败,假如失败需要重新把消息写入队列,注意这里说的失败并不是业务解决的正常返回“失败”状态,而是因为少量异常起因导致业务解决没有正常完成的情况。Poi系统方需要重试时才会发送失败的通知。

要保证最终一致性,该方案还有一个关键点是消息队列本身的可靠性和写数据库和写消息队列事务的一致性。比方淘宝的notify的两阶段提交机制就是为了满足这一点。也可以参考其它技术文章平台有一篇技术文章《轻量级高可靠消息队列》

四、总结

对于单机单库系统,数据一致性可通过关系型数据库的事务来满足,而且ACID特性中的C是指强一致性,各数据库本身都支持,而且很成熟。

分布式系统则需要以BASE理论作为指导,即以基本可使用性和最终一致性作为目标。

远程RPC调使用是一致性问题主要起因,异步解耦+消息队列可作为分布式系统满足最终一致性的优秀方案。

欢迎工作一到五年的Java工程师朋友们加入Java架构开发:744677563

本群提供免费的学习指导 架构资料 以及免费的解答

不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 分布式系统一致性问题处理实战

发表回复