图数据库 | 18、高可用分布式设计(中)

上文我们聊了在设计高性能、高可用图数据库的时候,从单实例、单节点出发,一般有3种架构演进选项:主备高可用,今天我们具体讲讲分布式共识,以及大规模水平分布式

主备高可用、分布式共识、大规模水平分布式,我们都知道这3套系统的实现复杂度是从低到高渐进的,但这并不意味着复杂度更高的系统在不同的应用场景、用户需求、查询模式、查询复杂度、数据特征条件下就能获得更好的效果。


分布式共识系统

前面提到即便在最简单的主备系统架构中也可能发生系统内无法保证一致性的情况,这是因为任何分布式系统在本质上都是异步分布式。所有操作(网络传输、数据处理、发送回执、信息同步、程序启动或重启等)都需要时间来完成,任何交易、任何事务处理在一个实例内都是异步的,而在多个实例之间这种异步性会被放大很多。分布式系统设计最重要的一个原则就是与异步性共存,不追求完美的一致性,但可以假设整个系统在大部分时间内是正常工作的,即便部分进程或网络出现问题,整个系统依然可以对外提供服务。

分布式共识系统,特别是分布式共识算法就由此应运而生,被用来保证即便在分布式系统中出现了各种各样的问题,但是整体服务依然可以保持在线。

分布式共识算法有3个核心特性:合法有效(validated proposal)​、达成一致(reaching agreement或unanimity)​、快速终止(processcan be terminated)​。

合法有效指的是进程间的指令信息传播需要基于合理有效的数据,且能让有效的进程集合达成一致,并且最终形成共识,进而终止同步过程的时耗需要在合理的、较短的时间内完成。在高性能分布式系统中可毫秒级完成同步,而在较大规模跨地域的分布式共识系统中,可能会出现秒级甚至需要人工介入的分钟级形成共识,具体的终止时间延迟取决于具体的业务需求。

达成一致等同于形成共识,类似于多个进程间的民主选举,一旦它们形成了共识,任何参与了选举的进程都不再允许对结果产生异议或按照与结果不符的方式或内容执行任务——这种情况就是我们前面提到过的分布式系统无法解决的“两军通信问题”​“拜占庭将军问题”​。

图1示意了多任务(多进程)间形成共识的过程,这一过程与我们日常生活中的行为并无本质不同。

图1: 多任务间形成共识


A、B、C、D四个人在一起讨论晚上去哪里,一开始A提议去看电影,得到了B的赞同,但是C很快提出去吃晚餐,D赞同C的提议,随后B改口赞同C的提议,最终A也改为同意C的提议。此时,四人达成了晚上去吃晚餐的共识。这就是多任务共识形成的简单例子。在实际的分布式共识算法中还会引入如角色、阶段以及如何终止共识等问题,下面逐一分析。

形成共识的过程需要有明确的终止算法,否则就会出现悬而不决、无限等待的问题。例如当某个实例(进程)下线后,剩余进程如果无限等待其重新上线,或是如图1所示,A、B、C、D四个进程出现层出不穷的新方案,以至于四人永远无法达成共识。共识算法需要考虑这些情况,并规避其发生。本质上,无论采用何种跨进程的集群内通信方式,都要使用尽可能简洁的算法,让共识的达成(进而终止)代价较低。

那么,分布式共识系统应该采用何种通信手段呢?前面提到过网络系统的3种通信方式:中心化(广播式)​、去中心化(分层区域广播或多播式)以及点对点分布式,对小型分布式系统而言,最简单和最直接的方式是广播式,因为发起广播者与信息接收者之间的互动逻辑决定了这个交互过程是属于“尽人事听天命”类型的单向一次性传送模式,还是其他更可靠的交互模式。

理解这个过程需要考虑这样一种可能发生的情况,即如果发起者A在发出请求给B、C后下线,但是并没有向D发送,那么在广播算法中,就需要考虑加上B、C可以向D继续广播的逻辑。从通信的复杂度角度看,这样的实现就是一种“可靠广播”模式,在有N个实例的分布式系统中,其复杂度为O(N^{2}),显然,这种漫灌式重度通信模式(floodingcommunication)对于大型分布式系统是不合适的,但是它已经具备了通过冗余通信来实现系统完整性、一致性的特征。

下面分析如何在分布式共识通信的过程中保证消息的有序性,即至少需要实现2个功能:多消息的事务性(原子性、不可分割性)和序列性。

一种比较简单的原子广播算法是分布式KV项目Apache Zookeeper中的ZAB(Zookeeper Atomic Broadcast)​,它把Zookeeper集群内的所有进程分为两个角色:领导者(leader)和跟随者(follower)​,3种状态:跟随(following)​、领导(leading)和选举(election)​;它的通信协议分为4个阶段:启动选举阶段(leader election phase)​、发现阶段(discovery phase)​、同步阶段(synchronization phase)​、广播阶段(broadcasting phase)​。

在启动选举阶段(阶段0)​,某个进程在选举状态中开始执行启动选举算法,并找到集群内的其他进程投票成为领导者。

在发现阶段(阶段1)​,进程检查选票并判断是否要成为两个角色之一,被选举为潜在领导者的进程称作意向领导(prospective leader)​,之后该进程通过与其他跟随进程通信发现最新的被接受的事务执行顺序。

在同步阶段(阶段2)​,发现过程被终止,跟随进程通过意向领导更新的历史记录进行同步。如果跟随进程自身的历史记录不晚于意向领导的历史,则向意向领导进程发送确认信息。当意向领导得到了主体选举人(quorum)的确认后,它会发送确认(commit)信息,这时意向领导成为确认领导(established leader)​。该阶段的算法逻辑如下:

在广播阶段(阶段3)​,如果没有新的宕机类问题发生,集群会一直保持在本阶段。在此阶段,不会出现两个领导进程,当前领导进程会允许新的跟随者加入,并接受事务广播信息的同步。

ZAB的阶段1~3都采用异步的方式,并通过定期的跟随进程与领导进程间的心跳信息来探测是否出现故障。如果领导进程在预定的超时时间内没有收到心跳,它会切换至选举状态,并进入阶段0,同样地,跟随进程也可以在没有收到领导进程心跳后进入阶段0。

在ZAB的具体实现逻辑中,leader选举是最核心的部分,ZAB采用的策略是拥有最新历史记录的进程会被选举为leader,并且假设拥有全部已提交事务(commitedtransactions)的进程同样拥有最新的已提议事务(most recent proposed transaction)​。当然,这个假设的前提是集群内的ID序列化及顺序增长,这一假设让leader选举的逻辑大幅简化,因此称为快速领导选举(Fast Leader Election,FLE)​。即便如此,FLE过程中的判断逻辑,特别是各种边界情况的考量依然很多,下面是节选的参与选举进程的FLE实现代码逻辑: 

 

ZAB最早是作为Yahoo!内部Hadoop项目的一个子项目,后来被拆分独立出来作为一款分布式服务器间进程通信及同步框架,并在2010年后成为Apache开源社区中的一个顶级项目。从上面的伪码中就可以看出,ZAB所采用的算法逻辑和通信步骤较为复杂。类似地,Paxos类算法的实现对于跨地理区域的系统实例间时钟同步有着严苛的要求,且算法逻辑复杂,不容易被理解。头部企业谷歌在其Spanner系统中通过原子钟的帮助实现基于Paxos的大规模分布式共识系统,但是对缺少同等系统架构把控能力的企业而言,Paxos就显得门槛过高了。 

在2013年之后,更简单、可解释的分布式共识算法应运而生,其中最知名的是2014年DiegoOngaro提出的RAFT算法及一种称作LogCabin的代码实现。

 在RAFT算法中,集群内的每个共识算法参与进程有3种角色:候选者(candidate)​、领导者(leader)和跟随者(follower)​。

与Poxos通过时钟同步来保证数据(事务)全局顺序一致不同(但是类似于ZAB)​,RAFT把时间块切分为terms(选举任期,类似于ZAB中的epoch)​,在每个任期期间(系统采用唯一的ID来标识每个任期,以保证不会出现任期冲突)​,领导者具有唯一性和稳定性。

RAFT算法有3个主要组件(或阶段)​:选举阶段、定期心跳阶段和广播及日志复制阶段。

图2示意了在一个3节点的RAFT集群中客户端与服务器集群的互动,整个流程围绕领导者角色进程展开,可分解为10步,而这只覆盖了RAFT算法的广播及日志复制阶段。 

图2:RAFT集群C/S工作流程步骤分解

如图3所示,3种角色及所负责的任务内容如下:

· 跟随者,不会主动发起任何通信,只被动接收RPC调用(Remote Procedure Calls)​。

· 候选者,会发起新的选举,对选举任期进行增量控制,发出选票,或重启以上任务。

在该过程中,只有含有全部已提交命令的候选者会成为领导者,并通过RPC调用的方式通知其他候选者选举结果,并避免出现split vote(平票)的问题(在RAFT算法中,通过随机选举超时,例如在0.15~0.3s间随机制造超时来避免因两个实例的候选进程同时发出导致选票计数出现平票)​。

另外,每个进程都维护了自己的一套日志,在原生的RAFT算法实现中称为logcabin(木筏)​。

· 领导者,会定期向所有跟随者发送心跳RPC,以防止因过长的空闲时间而过期(和重新选举)​。领导者通常是最先面向客户端进程请求的,对日志进程进行添加处理以及发起日志复制,提交并更改自身的状态机,并向所有跟随者同步log。

图3:RAFT共识算法集群进程间的角色转换关系


RAFT描述的是一种通用的算法逻辑,它的具体实现有很多种,并且有很大调整空间。例如,原始的RAFT算法与一主多备的架构类似,任何时候只有一个实例在服务客户端请求。如果我们结合图数据库的可能查询请求场景,完全可以分阶段地改造为如下几种(难度从低到高)​。

· 多实例同时接收读请求负载:写入依然通过leader节点实现,读负载在全部在线节点间均衡。

· 多实例同时接收先读再写类请求负载:典型的如回写类的图算法,全部节点都可以承载图算法,回写部分先进行本地回写,再异步同步给其他节点。

 · 多实例同时接收更新请求负载并转发:写入请求可以发送给任意集群内节点,但是跟随者会转发给leader节点处理。

· 多实例同时处理更新请求:这是最复杂的一种情况,取决于具体的隔离层级需求,如果多个请求同时在多个实例上更改同一段数据,并且有不同的赋值,会造成数据的不一致性。在这种情况下实现一致性的最可靠途径就是对关键区域采用序列化访问。这也是本章反复提到的,任何分布式系统在最底层、最细节、最关键的部分一定要考虑到有需要串行处理的情况。

目前已知的RAFT算法可能远超100种,如ETCD、HazelCast、Hashicorp、TiKV、CockrochDB、Neo4j、Ultipa Graph、嬴图等,并且以各种编程语言实现,如C、C++、Java、Rust、Python、Erlang、Golang、C#、Scala、Ruby等,足以体现分布式共识算法及系统的生命力。

在基于共识算法的高可用分布式系统架构中,我们做了一个比较重要的假设,即大多数时候,系统的每个实例上都存有全量的数据。注意,我们限定的是“大多数时候”​,言下之意是在某个时间点或切片下,多个实例间可能存在数据或状态的不一致性,也因此需要在分布式系统内通过共识算法来实现数据同步,以形成最终的数据一致性。

下篇继续聊大规模水平分布式。最近很忙,不过老夫会尽快更文。


· END ·



(文/Ricky - HPC高性能计算与存储专家、大数据专家、数据库专家及学者)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/68405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python】第二弹---深入理解编程基础:从常量、变量到注释的全面解析

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】【Python】 目录 1、常量和表达式 2、变量和类型 2.1、变量是什么 2.2、变量的语法 2.3、变量的类型 2.4、动态类型特…

生产环境中常用的设计模式

生产环境中常用的设计模式 设计模式目的使用场景示例单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点- 日志记录器- 配置管理器工厂方法模式定义一个创建对象的接口,让子类决定实例化哪个类- 各种工厂类(如视频游戏工厂模式创…

YOLOv10改进,YOLOv10检测头融合RFAConv卷积,添加小目标检测层(四头检测)+CA注意机制,全网首发

摘要 空间注意力已广泛应用于提升卷积神经网络(CNN)的性能,但它存在一定的局限性。作者提出了一个新的视角,认为空间注意力机制本质上解决了卷积核参数共享的问题。然而,空间注意力生成的注意力图信息对于大尺寸卷积核来说是不足够的。因此,提出了一种新型的注意力机制—…

解锁C#语法的无限可能:从基础到进阶的编程之旅

目录 一、C# 基础语法 1.1 数据类型 1.2 变量与常量 1.3 运算符 1.4 控制流语句 二、C# 面向对象编程语法 2.1 类与对象 2.2 封装 2.3 继承 2.4 多态 虚方法 抽象类 接口 三、C# 高级语法 3.1 特性(Attribute) 预定义特性 自定义特性 3…

“AI智能防控识别系统:守护安全的“智慧卫士”

在如今这个科技飞速发展的时代,安全问题始终是大家关注的焦点。无论是企业园区、学校校园,还是居民社区,都希望能有一双“慧眼”时刻守护着,及时发现并防范各种安全隐患。而AI智能防控识别系统,就像一位不知疲倦、精准…

Leetcode 983. 最低票价 动态规划

原题链接&#xff1a;Leetcode 983. 最低票价 class Solution { public:int mincostTickets(vector<int>& days, vector<int>& costs) {int n days.size();int last days[n - 1];int dp[last 1];map<int, int> mp;for (auto x : days)mp[x] 1;dp…

Vue篇-07

Vue UI组件库 一、移动端常用的UI组件库 1.1、Vant 1.2、Cube UI 1.3、Mint UI 二、PC端常用的UI组件库 2.1、Element UI Element - The worlds most popular Vue UI framework 安装&#xff1a; 按需引入&#xff1a; 135_尚硅谷Vue技术_element-ui按需引入_哔哩哔哩_b…

2025.1.15——四、布尔注入

题目来源&#xff1a;ctfhub技能树 目录 一、基本操作&#xff1a;整理已知信息&#xff0c;得到本题为布尔注入 方法一&#xff1a;手工盲注&#xff08;不推荐&#xff09; step 1&#xff1a;判断具体形式 step 2&#xff1a;查询字段数 step 3&#xff1a;通过回显判…

基于SpringBoot的装修公司管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

小游戏前端地区获取

目前前端获取除了太平洋&#xff0c;没有其它的了。 //在JS中都是使用的UTF-8&#xff0c;然而requst请求后显示GBK却是乱码&#xff0c;对传入的GBK字符串&#xff0c;要用数据流接收&#xff0c;responseType: "arraybuffer" tt.request({url: "https://whoi…

Spark 之 Aggregate

Aggregate 参考链接: https://github.com/PZXWHU/SparkSQL-Kernel-Profiling完整的聚合查询的关键字包括 group by、 cube、 grouping sets 和 rollup 4 种 。 分组语句 group by 后面可以是一个或多个分组表达式( groupingExpressions )。 聚合查询还支持 OLAP 场景下的多…

计算机网络 网络层 2

IP协议&#xff1a; Ip数据报的格式&#xff1a; 首部:分为固定部分 和 可变部分 固定部分是20B 版本&#xff1a;表明了是IPV4还是IPV6 首部长度&#xff1a;单位是 4B&#xff0c;表示的范围是&#xff08;5~15&#xff09;*4B 填充&#xff1a;全0&#xff0c;,让首部变…

JAVA-二叉树的四种遍历

目录 一、二叉树的存储 二、二叉树遍历的概念 1.前序遍历 2.中序遍历 3.后序遍历 4.层序遍历 三、概念面试题 四、代码实现 1.前序遍历 2.中序遍历 3.后序遍历 4.层序遍历 五、其他写法(非递归) 1.非递归前序遍历 2.非递归中序遍历 3.非递归后续遍历 一、二叉树…

Spring FactoryBean到仿照mybatis @Mapper的实现

目录 FactoryBean原理FactoryBean例子org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean mybatis mapper bean的手动实现思考复习下Jdbc传统sql查询做法Mapper接口实现思路复习批量注册beanDefinition: ConfigurationClassPostProcessor自定义实现Mapp…

【Go】Go数据类型详解—数组与切片

1. 前言 今天需要学习的是Go语言当中的数组与切片数据类型。很多编程语言当中都有数组这样的数据类型&#xff0c;Go当中的切片类型本质上也是对 数组的引用。但是在了解如何定义使用数组与切片之前&#xff0c;我们需要思考为什么要引入数组这样的数据结构。 1.1 为什么需要…

flutter Get GetMiddleware 中间件不起作用问题

当使用 get: ^5.0.0-release-candidate-9.2.1最新版本时&#xff0c;中间件GetMiddleware各种教程都是让我们在redirect中实现&#xff0c;比如&#xff1a; overrideRouteSettings? redirect(String? route) {return RouteSettings(name: "/companyAuthIndexPage"…

【Idea启动项目报错NegativeArraySizeException】

项目场景&#xff1a; Idea启动项目报错&#xff08;打包不报错&#xff09;&#xff0c;项目在服务器部署运行没有问题&#xff0c;尝试了重启idea、重启电脑、maven clean/install 都不行 maven-resources-production:sample: java.lang.NegativeArraySizeException: -5833…

微信小程序:播放音频

在小程序开发中&#xff0c;音频播放是一个重要的功能。本文将详细介绍小程序音频播放的相关知识点&#xff0c;帮助开发者更好地掌握小程序音频播放的实现方法。 一、小程序音频播放的基本流程 在小程序中&#xff0c;音频播放的基本流程如下&#xff1a; 获取音频数据&#…

运行fastGPT 第四步 配置ONE API 添加模型

上次已经装好了所有的依赖和程序。 下面在网页中配置One API &#xff0c;这个是大模型的接口。配置好了之后&#xff0c;就可以配置fastGPT了。 打开 OneAPI 页面 添加模型 这里要添加具体的付费模型的API接口填进来。 可以通过ip:3001访问OneAPI后台&#xff0c;**默认账号…

RocketMQ 学习笔记01

一、MQ简介 1. 什么是MQ&#xff1f; MQ&#xff08;Message Queue&#xff0c;消息队列&#xff09; 是一种在分布式系统中用于实现进程间通信和数据传输的中间件。它通过在不同进程或应用程序之间传递消息&#xff0c;实现数据的异步处理、解耦和削峰填谷等功能。MQ广泛应用…