1320143612856aead49579f386fcd447
漫谈分布式系统(15) -- 扩展性的最后障碍

系列第一章我们讲过,分布式系统把数据的存储和计算切分到多个节点,获得了横向扩展性

横向扩展的威力无穷,理论上有多少节点,就能汇聚多少存储和计算能力。

从这个角度看,分布式系统有发展成去中心化系统(Decentralized System)的可能。但由于架构设计上的问题,却导致了至少在大数据领域,分布式系统大都不是去中心化系统,反倒都有「中心」。

也由于这些中心的处理瓶颈,反过来导致了分布式系统往往没法无限扩展下去

master-slave 架构导致的中心化问题

具体来说,很多分布式系统出于各自的考虑,都采用了 master-slave 的架构。

下面我们以前面文章提到最多的 HDFS 和 YARN 为例,一起看下这种设计导致的问题。

HDFS 的中心化架构

HDFS 采用 master-slave 架构,是为了统一维护元数据。

回顾下系列第 3 篇文章讲过的 HDFS 读写数据的流程。

这个是写数据的简化流程图:

这个是读数据的简化流程图:

两个图都只是示意图,没有包含 pipeline 写、packet 拆分等细节。不过足以说明问题。

从这两个图可以看到,无论是读还是写数据,都必须首先去 NameNode(以下简称 NN) 访问元数据。

而经过前面几篇数据一致性文章的讲解,元数据统一管理的好处显而易见:数据一致性得到保障。

这点非常重要,数据被拆成 block 分散在各个机器上,元数据一旦不一致,数据哪怕还在,也没法拼回可用的文件了。

但是,元数据也是数据,也是有资源开销的。而单点 NameNode (HA 也只是 standby 一个实例)的资源是有限的,横向扩展下去,总有一天会遇到瓶颈。

NN 的内存开销主要有两部分:

  • 存量数据的元数据,常驻内存老年代,可以明确算出来。
  • 增量数据和操作消耗,快速进出新生代,视集群文件操作繁忙程度而定。

一个文件或 block 对象的内存开销是 150 bytes,由此可以推算出第一部分的实际开销,再给第二部分预留些余量,推荐的设置是每 100 万 block 给 1GB 内存。所以,当你有 1 亿个文件或 block 对象时,需要的内存大概是 100GB。

下面这个图是我之前在一个生产集群截的 NN 的内存情况,当时文件 + block 对象一共大概 1.5 亿。

很明显,当数据量持续增长下去,NN 的内存需求就会突破服务器的物理内存。NN 就会变成集群的性能瓶颈,甚至直接拖垮整个集群

YARN 的中心化架构

Hadoop 对计算资源采用 master-slave 架构,是为了统一调度计算资源和任务。

调度计算资源,是为了提高整体利用率;调度任务是为了组织任务执行过程。

由于不用像 HDFS NN 那样存储数据,计算资源和任务调度的性能压力更多体现在处理能力上。对外表现为处理延时(latency)。

下面是 Hadoop 第一代计算资源调度框架,即所谓 MRv1 架构图。

可以看到,资源的调度和任务的调度,这两个职责都压在 JobTracker 的身上

随着集群规模变大导致需要调度的计算资源变多,以及数据量和业务处理程序变多导致需要调度的任务变多,都使得在服务器有限的资源下,JobTracker 的性能开始成为集群的瓶颈。

很明显,任务调度的开销,增长的速度会比资源调度快的多。毕竟上新程序比上新节点成本低的多也快的多。所以,有了所谓的 MRv2(YARN),也是现在大部分公司的主流架构:

JobTracker 的职责被一分为二,资源调度的职责保留给新角色 RM,任务调度的职责拆给了新角色ApplicationMaster,并且 AM 和任务是一对一伴生的关系,不会遇到性能瓶颈。

但是还不够,资源的调度依然可能存在性能瓶颈。

上图也是我之前从一个生产系统取的,可以看到,平均的资源调度耗时在 2ms 左右。但随着任务数的增加,这个数据会逐渐上升,直至拖累整个集群。

解决分布式系统的中心化问题

HDFS NameNode 的去中心化

经过系列前几篇关于 partitioning 的文章的洗礼,应该不难想到,解决 HDFS NameNode 中心化问题的思路,就是拆分

多弄几个 NameNode,每个 NameNode 只负责维护一部分元数据,这样,NN 也就能横向扩展下去了。

怎么拆分呢?

HDFS 是文件系统,而文件系统是以目录树的形式组织的,很自然能想到根据目录去拆分。

社区按这个思路提出了 HDFS Federation 的实现。把目录拆分后,像 Unix 系统一样以挂载点(mount point)的形式分配给不同的 Nameservice,来提供服务。

每个 DataNode 仍然向所有 NameNode 汇报,只是会为它们维护独立的 block pool。所以,在物理上,实际存储资源仍然是 NN 们共享的,切分主要体现在逻辑上。

例如下图,划分了 3 个 NS,并分别挂载了不同的目录。

这样,内部拆分就做好了,每个 NS 都独立完整地服务自己负责的目录,互相之间没有交互,甚至不知道彼此的存在。

剩下的,就是怎么对外提供统一的视图了。

HDFS 是个比较复杂的系统,为了减小改动以免影响稳定性,社区提出了在客户端实现统一视图的方案,即所谓 ViewFS Federation

top Created with Sketch.