大数据处理系统——> 分布式计算系统
本课程/教材
课程教材:《分布式计算系统》
分布式系统
从数据管理的角度看分布式系统
分布式计算系统
课程组织结构
《分布式系统:概念与设计》认为:分布式系统是若干独立计算机的集合,这些计算机对于用户来说是就像一个单机的系统。
基于计算机构建的分布式系统
基于微型设备构建的分布式系统
第一代:层次、网状数据库系统
第二代:关系数据库系统
第三代:数据仓库系统
第四代:大数据管理系统
机遇与挑战
数据量大:要求大数据管理系统能够存储管理 TB 甚至 PB 级别的数据。
数据种类多:要求大数据管理系统能够在同一平台处理多种类型的数据。
产生速度快:要求大数据管理系统能够在数据产生的过程中实时处理数据(ms)。
潜在价值高:要求大数据管理系统能够支持使用机器学习、数据挖掘、数据分析等。
数据质量低:要求大数据管理系统能够支持更复杂的数据治理,数据分析和机器学习技术。
PS:总结就是,大,功能强,速度快
从“一劳永逸”走向分类定制。
不可能一劳永逸的原因:社交网络、知识图谱都不是结构化数据。
作用于若干独立计算机之上,使得这些计算机能够协同执行计算完成某项应用的软件系统。
归根结底是为了解决某些类别的应用问题而设计的分布式系统。
分类
来源 Doug Cutting 开发的文本搜索库
诞生于 2006 年,MapReduce+HDFS
集群网络拓扑
在没有 HDFS 之前遇到的一些问题:
HDFS:
HDFS 角色
NameNode
SecondaryNameNode
DataNode
NameNode 管理的工具
执行流程:
客户端向 NameNode 发起文件操作请求。
NameNode 反馈
读写操作客户端获取位置信息后再与 DataNode 进行读写交互。
文件块存放策略
第一个副本
第二个副本
第三个副本
NameNode 告知客户端文件每个数据块存储在何处。
客户端将数据块直接传输到指定的数据节点。
一次写入多次读写
故障类型
根据 SecondaryNameNode 中的 FsImage 和 Editlog 数据进行恢复。
为什么需要 HDFS,没有 HDFS 会是怎么样的?
HDFS 包括哪些部分,都有哪些作用?
详见 2.2.1 架构图。
MPI 是一个信息传递应用程序接口,包括协议和语义说明。
局限性:
将数据抽象成键值对,在处理中对键值对进行转换。
Map+Reduce
物理上,分而治之的思想,由多个任务并行计算完成。
抽象架构图
Hadoop MapReduce 架构图
从外部看不到是 map 任务还是 reduce 任务。
JobTracker
TaskTracker
MapReduce 是多进程方式,而 Spark 和 Flink 是多线程方式。
MapReduce 和 HDFS 的关系:
流程:
输入(HDFS)——>Map——>shuffle——>reduce——> 输出(HDFS)
会有文件分块存储,可能存在跨块记录。Map 读取单位 Split。
Split vs Block
[k1,v1]——>List([k2,v2])
典型的二路归并排序的过程。
Map 任务的数量通常由 Split 数量决定。
何时 shuffle?
与 Map 的过程有相似,也是有归并排序的过程。
Reduce 任务的数量
按照用户写定的 Reduce 任务来执行。
输入是一个文件,输出是一组文件。
与输入数据节点相反,MapReduce 需要定义输出文件的格式,即 OutputFormat。
MapReduce1.0 没有处理 JobTracker 故障机制,成为单点瓶颈,所有作业都要重新执行。
心跳机制维持。
连接不上,JobTracker 安排其他 TaskTracker 重新运行失败的任务。
过程对用户透明。
Reduce 任务重新从本地磁盘上读取数据。
当一个任务经过最大尝试次数运行后仍然失败,整个作业都会被标记失败。
优化方案
Question
两个键值对 <“a”,1>
和 <“a”,1>
,如果合并,会得到 <“a”,2>
,如果归并,会得到 <“a”,<1,1>>
起到一个广播的作用,将要处理的数据集进行分布式缓存,提高读取的速度。
最初是基于内存计算的批处理系统,现在也支持内外存同时使用。
Spark 曾在 2014 年,用十分之一的资源比 Hadoop 快 3 倍。
局限性
RDD(Resilient Distributed Dataset)
操作算子:创建算子,转换算子,action 算子
创建算子
转换算子
action 算子
从算子操作角度描述计算的过程
从 RDD 变换角度描述计算过程
RDD Lineage(DAG 有向无环图拓扑结构)
RDD 性质
**集群管理器(Cluster Manager):**负责管理整个系统的资源、监控工作节点。
执行器(Executor):负责任务执行
**驱动器(Driver):**负责启动应用程序的主方法并管理作业的运行。
Spark 架构实现了资源管理和作业管理两大功能的分离。
Standalone 架构图
Driver 如何体现?
逻辑上,Driver 独立于主节点、从节点和客户端,但会根据应用程序的部署方式存在。
客户提交应用程序时可以选择 Client 或 Cluster 部署方式。
Spark VS Hadoop
RDD 依赖关系
Stage 划分方法
基于 Operator DAG 的 Stage 划分
Stage 类型
ShuffleMapStage
ResultStage
所有依赖都是窄依赖,可以实现 pipeline(流水线)方式进行数据传输。
流水线是非阻塞方式,而 Shuffle 是阻塞方式。
ShuffleMapTask vs ResultTask
总共需要 10 个 Task。
所有依赖都是宽依赖,只能 Shuffle 进行。
Shuffle 发生在两个 ShuffleMapStage 之间或者 ShuffleMapStage 与 ResultStage 之间。从 Task 层面看,过程表现为两组 ShuffleMapTask 之间或者一组 ShuffleMapTask 与一组 ResultTask 之间的数据传输。
Application:用户编写的 Spark 应用程序
Job:一个 Job 包含多个 RDD 及作用于相应 RDD 转换操作,最后都是 action。
Stage:一个 Job 会分为多组 Task(即 Stage),也被称为 TaskSet。
Task:运行在 Executor 上的工作单元。
故障类型
RDD 存储机制
RDD 达到相应存储空间的阈值,Spark 会使用置换算法将部分 RDD 的内存空间腾出。
不做声明,RDD 会直接被丢弃。
RDD 提供持久化(缓存)接口
Lineage 机制
红色部分丢失,需要重新计算紫色部分。
与数据库恢复的比较
前述机制不足之处:
检查点将 RDD 写入到外部可靠的分布式文件系统(如 HDFS)。实现层面,写检查点是个独立的作业,在用户作业结束后进行。
优化方案
MapReduce 要求用户自定义 Combine 方法,而 Spark 内置了 combine 机制,简化了用户编程。
Spark 中表连接遇到大小表问题:
因为 Shuffle 开销很大,所以将小表广播出去,避免大表进行 Shuffle。
属于迭代实现 Job 的过程,但是整体是一个 Application。
MapReduce 迭代间数据传递需要借助读写 HDFS 完成,而 Spark 迭代间的 RDD 数据可以直接驻留在内存中。
特点:每个数据点都要与所有聚类中心进行计算。
通过广播的形式进行优化,减少了 shuffle 的次数。
以 PageRank 为例,每五轮迭代对网页排名写一次检查点。
写检查点前缓存网页排名的 RDD。
Question
以空间换时间的一个例子。
满足调试和生产的需求。
MapReduce1.0 的缺陷
资源管理与作业紧密耦合
作业的控制管理高度集中
系统
Yarn 应用
Yarn 管理的粒度是应用
Yarn | Spark | MapReduce |
---|---|---|
应用 | application | Job |
ResourceManager
**资源管理器:**负责整个系统的资源管理和分配
资源调度器(Resource Scheduler):分配 Container 并进行资源调度。
应用程序管理器(Application Manager):管理整个系统中运行的所有应用。
**NodeManager:**负责每个节点资源和任务管理
**ApplicationMaster:**当用户基于 Yarn 平台提交一个框架应用,Yarn 均启动一个 AM 用于管理该应用。
Container:资源的抽象表示,包括 CPU、内存等资源,是一个动态资源划分单位。
当 AM 向 RM 申请资源时,RM 向 AM 返回以 Container 表示的资源。
Yarn 的优势
RM 中的调度器维护了一个或多个应用队列。每个队列有一定的资源,同一队列共享资源。
Yarn 进行资源分配的对象是应用,用户提交的每个应用会分配到其中一个队列中,而队列决定了该应用能使用的资源上限。
资源分配策略
先到先得,等待时间长。
维护了层级式的队列,避免了某一长时间运行的应用独占资源的情况,但是会有资源空闲的问题造成浪费。
队列之间资源共享。
Resource Manager 故障
Node Manager 故障
Application Master 故障:重启
Container 中的任务故障:重启
Yarn-Cluster 模式
Yarn-client 模式
Yarn-client vs Yarn-cluster
Yarn-client
Yarn-Cluster
**设计目标:**将实现复杂的分布式一致性服务封装为一个高效可靠的原语集,并以一系列简单易用的接口提供给用户。
**工作原理:**存储元数据或配置信息,以便协调服务。
ZooKeeper 维护类似文件系统的层次结构,数据模型为一棵树。每个数据节点称为 Znode,每个 Znode 均保存信息。
Znode
ZooKeeper 允许用户为 Znode 添加顺序,如图上/app1/p1_1。
ZooKeeper 节点包含一组服务器,用于存储 ZooKeeper 的数据,每台服务器均维护一份树形结构数据的备份。服务器种类:
**领导者:**通过选主过程(选主算法)确定,直接为客户端提供读写服务。
**追随者:**仅能提供读服务,客户端发给追随者的写操作要转发给领导者。
**观察者:**作用与追随者类似,区别在于不参与选举领导者过程,该角色可选。
客户端与某一服务器通过会话连接,用心跳保持连接。客户端能够接收来自服务器的 Watch 事件通知。Watch 事件是指客户端可以在某个 Znode 中设置一个 Watcher。可以返回服务器变化的通知。
ZooKeeper 属于轻量级的数据存储系统,维护数据多个副本。
ZooKeeper 要保证各个节点之间副本的一致性,所以需要确定领导者。
分布式一致性协议用于解决分布式系统如何就某个提议达成一致的问题,paxos 是实现该协议的经典算法之一。其核心思想在于由某些节点发出提议,然后由其他节点进行投票表决,最后所有节点达成一致。
客户端返回写请求时,追随者、观察者、领导者都可以响应请求,前两者可能不是最新的,因此可以调用 sync()等待数据同步。
领导者故障:重新选举。
追随者或观察者故障:不影响整体,恢复的时候从领导者或其他节点同步数据。
为了表示各种资源,需要创建命名空间,从而方便在命名空间定位所需的资源。
如图就是用户上传名为“hdfs://app/test.txt”的文件的过程。
为了防止从节点故障,因此从节点状态监控、多主节点选主的方式必不可少。
从节点状态监控:
通过创建临时 Znode 实现,以 MapReduce 为例,TaskTracker 启动在/cluster 下创建一个临时 Znode,一旦 TaskTracker 故障,断开与 TaskTracker 的连接,Znode 被删除。
选主问题
为了防止单个 JobTracker 存在单点故障问题,需要配置多个 JobTracker 实现 MapReduce 系统的高可用性。主节点以 Znode 下标小的为主节点,一旦宕机交给后一位。
分布式系统在运行过程中通常需要动态修改配置。进程 A 创建配置后,如果进程 B、C 需要使用,需要不断监听 A 的更新通知,一旦更新,读取/config 配置进行相应操作。
双屏障机制(对于一组迭代作业)
**MapReduce、Spark:**静态数据
**Storm:**动态实时数据(流数据)
Storm 流计算系统处理数据以流的形式存在。
批处理中,任务短时进行,处理完毕数据即终止。
流计算需要长期运行,理论上无界。计算任务长期驻留在计算节点并且更新自身的状态。
**状态:**一种特殊数据,维护部分计算结果(后期 Storm 才维护状态)。
Storm 将流数据看作一个无界的、连续的元组序列。
**元组:**一个元组对应一条记录,一条记录包含若干字段。
Storm 使用拓扑抽象描述计算过程,拓扑是由 Spout 和 Bolt 组成的网络。
拓扑在逻辑上是一个有向无环图(DAG)。
边描述了数据流动的方向。
**Spout:**流数据源头,负责从数据源不断读取数据,然后封装成元组发送给 Bolt。
**Bolt:**描述针对流数据转换过程,内部封装了消息处理逻辑,负责将接收的流数据转换为新的流数据(过滤、聚合、查询等操作)。
拓扑中 Bolt 在物理上由若干任务实现。
Storm 采用主从架构,主要工作部件包括:Nimbus、Supervisor、Worker 和 ZooKeeper。Nimbus 位于主节点,Supervisor 和 Worker 位于从节点。主从节点协调依赖于 ZooKeeper。
其中,Nimbus 和 Supervisor 作为 Storm 的系统服务启动,Worker 进程随着应用程序执行而启动。Executor 线程执行同一个组件(Spout 或 Bolt)的一个或多个任务,任务是以对象形式存在的代码。
Storm、MapReduce、Spark 对比
系统 | MapReduce | Storm | Spark |
---|---|---|---|
系统进程 | JobTracker | Nimbus | Master |
TaskTracker | Supervisor | Worker | |
Child | Worker | CoarseGrainedExecutorBackend | |
工作线程 | - | Executor | Task |
任务代码 | Task | Task | |
基础接口 | Map\Reduce | Spout/Bolt | RDD API |
一个 Executor 线程通常仅执行一个任务,多个任务串行执行(并且属于同一组件)。
为了完成元组传输,需要解决以下两个问题:
实际上,Storm 通过流数据分组策略确定上游组件的任务发送给下游组件任务的元组,并且发送的过程一次只传递一条元组。
流数据分组策略定义了两个存在订阅关系的组件之间进行元组传输的方式。常见的分组策略:
一次一元组(一次一记录)
作为对比,MapReduce 中 Reduce 需要等 Map 阶段处理完毕才能从磁盘中获取 Map 结果,Spark 中 Stage 必须将结果写入到磁盘中去,下一个 Stage 中的任务才能够读取这些结果。
故障类型:
可靠的容错保障应该保证故障前后消息的一致性。容错语义等级:
Storm 采用 ACK 机制达到至少一次的容错语义。
Spout 中的每条元组对应一棵元组数。在 Storm 中,Spout 图发出元组时用户可以为其指定标识,称为 STid。同一个 STid 的元组构成一棵元组数。
**Acker:**一类特殊的任务,负责跟踪 Spout 发出的元组及其元组数。默认为 1。
**Mid:**元组树中元组传输在物理上表现的消息传输,包含的 64 位标识。如果正常传输,上游与下游组件任务接收消息的 Mid 相同。
ACKer 端维护
当 Acker 数量大于 1 时,使用一致性哈希将一个 STid 对应到 Acker,从而多个 Acker 互不干扰。
如果 Worker 进程发生故障导致部分消息丢失,Acker 在设定时间内无法接收拓扑末端 Bolt 发送的 STid 确认消息,或者 STid 对应的 ack_val 不为 0。Storm 选择重新发送 STid 为标识的元组。
实际上是 Spark 核心 API 的一个扩展,能够将连续的流数据进行离散化后交给 Spark 批处理系统。实现了利用批处理系统支持流计算。
微批处理方式将一定时间间隔内的数据视为一批静态的数据,将其交由批处理系统处理。
Spark Streaming 采用微批处理方式,将连续的流数据进行切片,生成一系列小块数据。
输入数据的 DStream 由一组 RNN 序列构成,这些 RDD 均会得到相同的处理,可以针对 RDD 执行一系列操作进行变换,构成 Operator DAG。
在物理层面,底层的 Spark 利用分布式架构加快数据处理,因而 DAG 中的操作算子实际上由若干实例任务实现。
物理架构上与 Spark 相同,不同的地方在于,Spark Streaming 对驱动器和执行器部件进行了扩充。
Spark Streaming 需要将 DStream 的转换操作转换为 Spark 中对 RDD 的转换操作,生成关于 RDD 操作的 DAG。
对于一个 Spark Streaming 应用程序来说,其输入数据可以来自一个或多个流。系统接收流数据的方式有:
前者需要确保数据在两个工作节点得到备份才向客户端发送确认消息,后者由于有 HDFS 等存储系统所以不用备份。
DStream 转换操作:
DStream 的转换操作由 RDD 转换操作封装而成,并由 Spark Streaming 翻译为一个或多个 RDD 转换操作。因此 Spark Streaming 将描述 DStream 转换的 Operator DAG 转变为描述 RDD 转换的 OPerator DAG,交给底层的 Spark 批处理引擎,进而针对不断到达的小批量数据生成一系列的作业。
Spark Streaming 支持以时间为单位的窗口操作。
滑动窗口这种非增量的方式会造成大量重复计算。
**状态:**如果某操作中保存的数据将在新数据到达后进行的计算中重新使用,则该保存的数据即为状态。
Spark Streaming 中的状态即为系统运行过程中产生的 RDD。
在 Spark Streaming 中,一张由 DStream 操作组成的 DAG 存在一个输出操作。与之对比,Spark 中遇到行动操作触发 DAG 生成,而 Spark Streaming 没有行动操作的概念,而是依据输出操作生成 DAG。
底层 Spark 引擎执行的是由 RDD 操作组成的 DAG。
故障类型:
如果某个执行器故障且不包含 Receiver 任务,则表示只有负责数据处理的任务受到了影响。所以可以利用 Lineage 机制进行容错。
对于含有 Receiver 的执行器故障问题,采用基于日志的容错策略。
对于外部数据源获取的数据,Spark Streaming 将其被分在两个工作节点中,如果执行器故障,从备份节点重新获取即可,如果备份节点同时发生故障,只能宣告失败。
**问题:**重启后的执行器重新获取数据,涉及获取哪些数据以及重复读取的问题。
**解决方案:**将数据以日志形式存入外部的文件系统(HDFS)中。当执行器故障重启后,Receiver 从外部文件系统加载日志并重新读取输入数据,确保不重复读取日志中已存在的数据。
防止 RDD Lineage 过程导致恢复过程开销大,结合检查点避免重计算。
为了支持驱动器故障恢复,需要设计元数据检查点。
系统运行时,将元数据检查点写入可靠的外部文件系统。当发生驱动器故障时,从外部文件系统加载元数据检查点和日志。
检查点:
Spark Streaming 能够保证准确一次的容错语义。但是从流计算处理整个流程来看,为了使端到端的容错语义达到准确一次,还需要提供数据源和接收处理结果的系统能够支持准确一次的容错语义。
批处理适合处理大批量数据,对实时性要求不高,流处理适合快速处理产生的数据,对实时性要求较高的场景。同一场景的不同模块可能对实时性有不同的要求,这就是批流处理的价值。
如微博,历史数据适合批处理,新数据适合流处理。
任何针对数据的查询均可使用以下等式表达:
q u e r y = f u n c t i o n ( a l l d a t a ) query=function(all \ data) query=function(all data)
数据特别大的场景难以快速响应。
预先物化视图
针对查询预先计算,得到批处理视图(batch view)
b a t c h v i e w = f u n c t i o n 1 ( a l l d a t a ) batch \ view=function_1(all \ data) batch view=function1(all data)
q u e r y = f u n c t i o n 2 ( b a t c h v i e w ) query = function_2(batch \ view) query=function2(batch view)
但是数据往往快速动态增加,因此批处理视图结果存在滞后性。
解决方案:三层结构
实际获取所有数据并进行查询通常难以实现。Lambda 架构将所有数据视为**主数据(master dataset)和新数据(new data)**的组合。
总体来说,Lambda 架构可以用以下三个等式表达:
b a t c h v i e w = f u n c t i o n 1 ( a l l d a t a ) batch \ view=function_1(all \ data) batch view=function1(all data)
r e a l − t i m e v i e w = f u n c t i o n 2 ( r e a l − t i m e v i e w , n e w d a t a ) real-time\ view=function_2(real-time\ view,\, new data) real−time view=function2(real−time view,newdata)
q u e r y = f u n c t i o n 3 ( b a t c h v i e w , r e a l − t i m e v i e w ) query = function_3(batch \ view, real-time\ view) query=function3(batch view,real−time view)
MapReduce 作为批处理层,Strom 作为加速层,分布式数据库 Cassandra 作为服务层。
优点:
平衡了重新计算高开销和需求低延迟的矛盾。
缺点:
Question:批处理引擎只能处理批数据,流计算引擎只能处理流数据?
未必,只是性能会有问题。
有界数据集:对应批数据
无界数据集:对应流数据
基于要素:
根据窗口大小和间隔之间的关系将窗口划分为:
基于时间的会话窗口,一般按照超时时间定义,任何在超时时间内的记录均属于同一个会话(和用户的浏览行为相关)。
将一条数据记录视为一个事件
事件时间和处理时间会存在偏差。引入水位线的概念,水位线是一个事件时间戳,其指示的事件时间表示早于该事件的时间记录已经完全被系统观测。
Dataflow 编程模型将所有输入数据视为无界数据集,有界数据作为无界数据的特例。编程模型处理的输入数据特点:
Dataflow 编程模型又称 WWWH(What-Where-When-How)模型,基本含义如下:
使用 PCollection
对于无界数据而言,通过窗口定义,可以仅针对窗口中的数据执行操作而不必获取所有数据。
描述何时将窗口的结果输出。当水位线越过窗口指定事件时间后触发结果的输出。
从关系数据模型角度来看,如果将键值对看做元组,则一系列键值对可以构成关系表。关系化的 Dataflow 编程模型将输入数据看做关系表,而非一系列键值对记录,输出同样为关系表。
早期关于流数据就有连续查询语言 CQL,用于针对流数据进行 SQL 的操作。
借鉴 CQL 的思想,关系化的 Dataflow 编程模型将输入的无界数据集转换为关系表,由于关系表是动态变化的,因而在触发器定义的时刻针对关系表进行 SQL 操作得到新的关系表,再将新的关系表相应地转换为无界数据集。
可以从两个层面理解批流统一编程:
**一体化执行引擎:**既能同时支持批处理和流计算,又支持统一编程。
Structured Streaming 实现了批处理统一编程,底层采用 Spark 批处理引擎,采用微批处理的执行模式。系统不断启动短时应用处理微批数据,处理完毕之间的延迟为秒级。
优点:
缺点:
代表为 Flink。系统一次性启动长时运行的应用。延迟大大低于微批处理的延迟。通常为毫秒级。
优势:
** 2008 年**
德国科学基金会资助 Stratosphere 研究项目。
批处理系统,扩充 MapReduce 算子,引入流水线方式进行数据传输。
将数据库与 MapReduce 中的优势结合在一起。
2015 年
谷歌发表 Dataflow 模型的论文
Flink 逐步定位为批流一体化的执行引擎并且支持 Dataflow 模型中定义的批流融合操作。
2019 年
阿里巴巴收购 Ververica
数据模型
Flink 将输入数据看作一个不间断、无界的连续记录序列。
Flink 将这一系列的记录抽象成 DataStream。
类似于 RDD,DataStream 是不可变的。
数据模型的比较
DataStream 操作算子
操作算子对 DataStream 进行变换
一系列的变换操作构成一张有向无环图 DAG
DataSource
描述数据来源。
Transformation
描述 DataStream 在系统中的转换逻辑。
DataSink
描述数据的走向,标志着 DAG 的结束。
一系列操作算子构成 DAG,描述计算构成:
通常来说 Flink 系统对应一个 DAG,而 Spark 中一个应用包含一个或多个 DAG。
因为 DataStream 是动态变换的,所以没办法固定节点(节点不断变化),对后面的容错没有意义。
迭代比较
Client
JobManager
根据逻辑执行图产生物理执行图,负责协调系统的作业执行,包括任务调度,协调检查点和故障恢复等。
Standalone 模式下:
TaskManager
执行 JobManager 分配的任务,并且负责读取数据、缓存数据以及与其他 TaskManager 进行数据传输。
Standalone 模式下
各个系统之间的比较
Yarn 模式架构
Standalone vs Yarn 模式
提交方式
Standalone 模式下,用户使用客户端提交 Flink 应用程序时,可以选择 Attached 方式或者 Detached 方式。
Yarn 模式应用程序执行流程
Yarn 下提交方式:
CliFrontend 同样也是 Attached 和 Detached 两种提交方式,但是连接或者断开的对象是 YarnJobClusterEntrypoint,相当于 appMaster。
用户编写的 DataStream 程序,Flink 的 Client 将其解析产生逻辑执行图 DAG。
**Chaining 优化:**将窄依赖算子合并成大算子。
任务分配
JobManager 将各算子任务分配给 TaskManager。
根据任务槽容量,尽可能将存在数据传输关系的算子实例放在同一个任务槽,保持数据传输的本地性。
对比 Spark,在 Spark 中,逻辑执行图 DAG 和物理执行计划 Stage 都在 Driver 中生成,而在 Flink 中,逻辑执行图在 Client 完成,物理执行图由 JobManager 完成。
流水线机制
数据没有存入磁盘中,只在 buffer 中,所以下游出现故障是无法从上游得到副本的。
Pipeline in Flink vs Spark
Task 之间的数据传输方式
迭代算子
数据反馈实现
流式迭代
批式迭代
JobManager 故障
什么是状态?
窗口算子所需维护的内容。
PS:注意需要区分算子的状态与进程/节点的状态。
为什么需要状态管理?
用户程序管理状态
因此,状态管理应该交给系统而不是用户。
**状态:**系统定义的特殊的数据结构,用于记录需要保存的算子计算结果。
有状态算子 VS 无状态算子
状态管理与容错
某时刻,流计算系统处理的记录可以分为三种类型:
虽然绝对同步时钟是不存在的,但是同时刻保存算子状态到检查点的目的是区分第一种记录和后两种记录。
异步屏障快照
**Chandy-Lamport 算法:**分布式系统中用于保存系统状态。
异步屏障快照算法
屏障
JobManager 在输入记录中插入屏障,这些屏障与记录一起向下游的计算任务流动。
异步
某个任务将标识为 n 的屏障对齐后,可继续接收属于检查点 n+1 的数据。
Flink 状态存储
故障恢复
当发生故障时
Flink 的容错能够满足准确一次的容错语义
在迭代中,仅有屏障无法区分属于检查点 n 和检查点 n+1 的情况。
根据 Chandy-Lamport 算法,反馈环路中的所有记录需要以日志形式保存起来。
故障发生,系统:
Flink vs Spark
Giraph 是一种大规模图处理的解决方案。
已有的图算法库或者 MapReduce 系统不具备以下所有特点:
图由顶点和边组成,Giraph 采用类似邻接表方式表示图数据。
表示一条边,该边的权重为 weight。
Giraph 采用以顶点为中心的计算模型。只有在顶点才执行相应计算。
Giraph 采用整体同步并行的迭代模型。
Giraph 的计算过程由一系列超步的迭代组成,每个超步的执行过程如下:
上图是编号更新任务,即将每个连通分量的标签更新为连通图中编号最小值,(a)和(b)是一个超步中局部计算(初始化编号)和全局通信的过程。下面的四个超步是完整的编号更新过程。
由于连通分量及单源最短路径等算法的计算过程满足取最小值这种递减的偏序特性,因此可以自动判断迭代是否停止(没有新消息的更新),但是对于 PageRank 和 K 均值聚类算法不满足该特性,通常需要设置最大超步数量控制迭代过程。
Pregel 架构(Giraph 的出处)由 Master 和 Worker 构成。其中,系统对图进行了划分,形成若干分区,每个 Worker 负责一个或多个分区并负责针对该分区的计算任务,而 Master 充当管理员的角色,负责协调各个 Worker 执行任务。
Worker
一个 Worker 管辖的分区信息保存在内存中,分区顶点的信息包含顶点和边的值、消息及标志位。
Master
协调 Worker 执行任务,每个 Worker 向 Master 注册,Master 为 Worker 分配唯一 ID。Master 维护 Worker 的 ID、地址、分区信息。
Giraph
Giraph 的实现借用了 MapReduce 框架。将所有的图处理逻辑在启动 Map 任务的 run 方法中实现,从 MapReduce 角度,执行 Giraph 作业仅启动了 Map 任务。
采用 ZooKeeper 的协调服务选主,确定某个 Map 任务为 Master。
对于 MapReduce 来说,虽然提交作业和启动 Map 任务与普通的 MapReduce 作业相同,但是在迭代计算过程,MapReduce 需要不断提交 MapReduce 作业, 反复从 HDFS 中读写(效率低),而 Giraph 自始至终仅呈现一个作业,迭代开始输入数据,迭代结束写入输出结果。
Giraph 工作过程可划分为数据输入、迭代计算和数据输出三个阶段。
Giraph 需要根据顶点将一张图划分为多个分区,即按照顶点标识决定该顶点属于哪个分区,每个分区包含一系列顶点和以这些顶点为起点的边。
输入数据的分区与 Giraph 需要的分区往往不一致,数据划分实际上是完成输入数据到 Giraph 期望分区的调整,该过程由 Master 和 Worker 共同完成。
超步开始,Worker 针对每个顶点调用 compute 方法,根据获取的前一个超步的消息更新该顶点的计算值。
VertexStore 存储顶点标识、计算值以及边的信息。MsgStore(t)存储超步 t-1 全局通信阶段各顶点获取的消息,该部分用于超步 t 中顶点的计算。MsgStore(t+1)存储超步 t 全局通信阶段各顶点获取的消息,该部分用于超步 t+1 中顶点的计算。
类似于消息,标志位同样存储两份,即 StatStore(t)和 StatStore(t+1),分别存储超步 t 和超步 t+1 中处于活跃状态的顶点。
总的来说,超步 t 中某以顶点的处理过程如下:
为了减少传输和缓存开销,Worker 在发送消息时将发往同一顶点的多个消息合并为一个消息。
如果用户使用了 Aggregator。每个顶点均可向某个 Aggregator 提供数据,Worker 将该内容汇报给 Master,Master 汇总后进行聚合操作产生下一个超步计算的值。
同步控制确保所有 Worker 均完成超步 t 后再进入超步 t+1,通过 Zookeeper 完成同步。
超步结束后,如果 Master 收到的活跃节点数为 0,表示迭代过程结束,此时 Master 通知 Worker 将计算结果持久化存储。
Master 故障,整个作业执行失败,Worker 故障,回滚到最近的检查点进行恢复。
Giraph 允许用户设置写检查点的间隔,即每隔多少超步写检查点。在需要设置检查点的超步开始时,Master 通知所有 Worker 将管辖的分区信息写入 HDFS 中,同时保存 Aggregator 的值。
设置检查点,则回滚到最近的检查点所在的超步,否则重新开始计算。
在这个过程中,即使只有一个 Worker 故障,所有 Worker 都要重新加载,导致了重复计算。
Pregel 的论文提出了局部恢复的策略,将恢复操作限制在丢失的分区上,从而提高恢复的效率。也就是将发送的消息同时以日志的形式记录,新启动的 Worker 仅从检查点恢复丢失的分区,其他 Worker 回放日志使得新 Worker 重新计算至发生故障的超步。