大数据知识体系入门
大数据就是将各种数据统一收集起来进行计算,发掘其中的价值。这些数据,既包括数据库的数据,也包括日志数据,还包括专门采集的用户行为数据;既包括企业内部自己产生的数据,也包括从第三方采购的数据,还包括使用网络爬虫获取的各种互联网公开数据。
面对如此庞大的数据,如何存储、如何有效利用大规模的服务器集群处理计算才是大数据技术的核心。
大数据技术发展史
今天人们常说的大数据技术,其实起源于 Google 在 2004 年前后发表的三篇论文,也就是我们经常听到的"三驾马车",分别是分布式文件系统 GFS、大数据分布式计算框架 MapReduce 和 NoSQL 数据库系统 BigTable。
搜索引擎主要就做两件事情,一个是网页抓取,一个是索引构建,而在这个过程中,有大量的数据需要存储和计算。这"三驾马车"其实就是用来解决这个问题的,从介绍中也能看出,一个文件系统、一个计算框架、一个数据库系统。
当时的天才程序员,也是 Lucene 开源项目的创始人 Doug Cutting 正在开发开源搜索引擎 Nutch,阅读了 Google 的论文后,他非常兴奋,紧接着就根据论文原理初步实现了类似 GFS 和 MapReduce 的功能。
两年后的 2006 年,Doug Cutting 将这些大数据相关的功能从 Nutch 中分离了出来,然后启动了一个独立的项目专门开发维护大数据技术,这就是后来赫赫有名的 Hadoop,主要包括 Hadoop 分布式文件系统 HDFS 和大数据计算引擎 MapReduce。
Hadoop 发布之后,Yahoo 很快就用了起来。大概又过了一年到了 2007 年,百度和阿里巴巴也开始使用 Hadoop 进行大数据存储与计算。
2008 年,Hadoop 正式成为 Apache 的顶级项目,后来 Doug Cutting 本人也成为了 Apache 基金会的主席。自此,Hadoop 作为软件开发领域的一颗明星冉冉升起。
同年,专门运营 Hadoop 的商业公司 Cloudera 成立,Hadoop 得到进一步的商业支持。
这个时候,Yahoo 的一些人觉得用 MapReduce 进行大数据编程太麻烦了,于是便开发了 Pig。Pig 是一种脚本语言,使用类 SQL 的语法,开发者可以用 Pig 脚本描述要对大数据集上进行的操作,Pig 经过编译后会生成 MapReduce 程序,然后在 Hadoop 上运行。
编写 Pig 脚本虽然比直接 MapReduce 编程容易,但是依然需要学习新的脚本语法。于是 Facebook 又发布了 Hive。Hive 支持使用 SQL 语法来进行大数据计算,比如说你可以写个 Select 语句进行数据查询,然后 Hive 会把 SQL 语句转化成 MapReduce 的计算程序。
这样,熟悉数据库的数据分析师和工程师便可以无门槛地使用大数据进行数据分析和处理了。Hive 出现后极大程度地降低了 Hadoop 的使用难度,迅速得到开发者和企业的追捧。据说,2011 年的时候,Facebook 大数据平台上运行的作业 90% 都来源于 Hive。
随后,众多 Hadoop 周边产品开始出现,大数据生态体系逐渐形成,其中包括:专门将关系数据库中的数据导入导出到 Hadoop 平台的 Sqoop;针对大规模日志进行分布式收集、聚合和传输的 Flume;MapReduce 工作流调度引擎 Oozie 等。
在 Hadoop 早期,MapReduce 既是一个执行引擎,又是一个资源调度框架,服务器集群的资源调度管理由 MapReduce 自己完成。但是这样不利于资源复用,也使得 MapReduce 非常臃肿。于是一个新项目启动了,将 MapReduce 执行引擎和资源调度分离开来,这就是 Yarn。2012 年,Yarn 成为一个独立的项目开始运营,随后被各类大数据产品支持,成为大数据平台上最主流的资源调度系统。
同样是在 2012 年,UC 伯克利 AMP 实验室(Algorithms、Machine 和 People 的缩写)开发的 Spark 开始崭露头角。当时 AMP 实验室的马铁博士发现使用 MapReduce 进行机器学习计算的时候性能非常差,因为机器学习算法通常需要进行很多次的迭代计算,而 MapReduce 每执行一次 Map 和 Reduce 计算都需要重新启动一次作业,带来大量的无谓消耗。还有一点就是 MapReduce 主要使用磁盘作为存储介质,而 2012 年的时候,内存已经突破容量和成本限制,成为数据运行过程中主要的存储介质。Spark 一经推出,立即受到业界的追捧,并逐步替代 MapReduce 在企业应用中的地位。
一般说来,像 MapReduce、Spark 这类计算框架处理的业务场景都被称作批处理计算,因为它们通常针对以"天"为单位产生的数据进行一次计算,然后得到需要的结果,这中间计算需要花费的时间大概是几十分钟甚至更长的时间。因为计算的数据是非在线得到的实时数据,而是历史数据,所以这类计算也被称为大数据离线计算。
而在大数据领域,还有另外一类应用场景,它们需要对实时产生的大量数据进行即时计算,比如对于遍布城市的监控摄像头进行人脸识别和嫌犯追踪。这类计算称为大数据流计算,相应地,有 Storm、Flink、Spark Streaming 等流计算框架来满足此类大数据应用的场景。
流式计算要处理的数据是实时在线产生的数据,所以这类计算也被称为大数据实时计算。在典型的大数据业务场景下,数据业务最通用的做法是,采用批处理的技术处理历史全量数据,采用流式计算处理实时新增数据。而像 Flink 这样的计算引擎,可以同时支持流式计算和批处理计算。
除了大数据批处理和流处理,NoSQL 系统处理的主要也是大规模海量数据的存储与访问,所以也被归为大数据技术。NoSQL 曾经在 2011 年左右非常火爆,涌现出 HBase、Cassandra 等许多优秀的产品,其中 HBase 是从 Hadoop 中分离出来的、基于 HDFS 的 NoSQL 系统。
HDFS
Google 大数据"三驾马车"的第一驾是 GFS(Google 文件系统),而 Hadoop 的第一个产品是 HDFS,可以说分布式文件存储是分布式计算的基础,也可见分布式文件存储的重要性。
在整个大数据体系里面,最宝贵、最难以代替的资产就是数据,大数据所有的一切都要围绕数据展开。HDFS 作为最早的大数据存储系统,存储着宝贵的数据资产,各种新的算法、框架要想得到人们的广泛使用,必须支持 HDFS 才能获取已经存储在里面的数据。所以大数据技术越发展,新技术越多,HDFS 得到的支持越多,我们越离不开 HDFS。HDFS 也许不是最好的大数据存储技术,但依然是最重要的大数据存储技术。
Hadoop 分布式文件系统 HDFS 的设计目标是管理数以千计的服务器、数以万计的磁盘,将这么大规模的服务器计算资源当作一个单一的存储系统进行管理,对应用程序提供数以 PB 计的存储容量,让应用程序像使用普通文件系统一样存储大规模的文件数据。
大容量存储和高速访问
HDFS 是在一个大规模分布式服务器集群上,对数据分片后进行并行读写及冗余存储。因为 HDFS 可以部署在一个比较大的服务器集群上,集群中所有服务器的磁盘都可供 HDFS 使用,所以整个 HDFS 的存储空间可以达到 PB 级容量。
下图是 HDFS 的架构图,从图中你可以看到 HDFS 的关键组件。

HDFS 的关键组件有两个,一个是 DataNode,一个是 NameNode。
DataNode 负责文件数据的存储和读写操作,HDFS 将文件数据分割成若干数据块(Block),每个 DataNode 存储一部分数据块,这样文件就分布存储在整个 HDFS 服务器集群中。应用程序客户端(Client)可以并行对这些数据块进行访问,从而使得 HDFS 可以在服务器集群规模上实现数据并行访问,极大地提高了访问速度。
NameNode 负责整个分布式文件系统的元数据(MetaData)管理,也就是文件路径名、数据块的 ID 以及存储位置等信息,相当于操作系统中文件分配表(FAT)的角色。HDFS 为了保证数据的高可用,会将一个数据块复制为多份(缺省情况为 3 份),并将多份相同的数据块存储在不同的服务器上,甚至不同的机架上。这样当有磁盘损坏,或者某个 DataNode 服务器宕机,甚至某个交换机宕机,导致其存储的数据块不能访问的时候,客户端会查找其备份的数据块进行访问。
下面这张图是数据块多份复制存储的示意。

HA(High Availability)架构模型
1. 数据存储故障容错
磁盘介质在存储过程中受环境或者老化影响,其存储的数据可能会出现错乱。HDFS 的应对措施是,对于存储在 DataNode 上的数据块,计算并存储校验和(CheckSum)。在读取数据的时候,重新计算读取出来的数据的校验和,如果校验不正确就抛出异常,应用程序捕获异常后就到其他 DataNode 上读取备份数据。
2. 磁盘故障容错
如果 DataNode 监测到本机的某块磁盘损坏,就将该块磁盘上存储的所有 BlockID 报告给 NameNode,NameNode 检查这些数据块还在哪些 DataNode 上有备份,通知相应的 DataNode 服务器将对应的数据块复制到其他服务器上,以保证数据块的备份数满足要求。
3. DataNode 故障容错
DataNode 会通过心跳和 NameNode 保持通信,如果 DataNode 超时未发送心跳,NameNode 就会认为这个 DataNode 已经宕机失效,立即查找这个 DataNode 上存储的数据块有哪些,以及这些数据块还存储在哪些服务器上,随后通知这些服务器再复制一份数据块到其他服务器上,保证 HDFS 存储的数据块备份数符合用户设置的数目,即使再出现服务器宕机,也不会丢失数据。
4. NameNode 故障容错
NameNode 是整个 HDFS 的核心,记录着 HDFS 文件分配表信息,所有的文件路径和数据块存储信息都保存在 NameNode,如果 NameNode 故障,整个 HDFS 系统集群都无法使用;如果 NameNode 上记录的数据丢失,整个集群所有 DataNode 存储的数据也就没用了。
NameNode 采用主从热备的方式提供高可用服务。在一个 HA 集群中,会配置两个 NameNode,一个是 Active NameNode(主),一个是 Standby NameNode(备)。主节点负责执行所有修改命名空间的操作,备节点则执行同步操作,以保证与主节点命名空间的一致性。
HA 架构模型如下图所示:

HA 集群中所包含的进程的职责各不相同:
- Active NameNode:负责执行整个文件系统中命名空间的所有操作;维护着数据的元数据,包括文件名、副本数、文件的 BlockId 以及 Block 块所对应的节点信息;另外还接受 Client 端读写请求和 DataNode 汇报 Block 信息。
- Standby NameNode:它是 Active NameNode 的备用节点,一旦主节点宕机,备用节点会切换成主节点对外提供服务。它主要是监听 JournalNode Cluster 上 editlog 变化,以保证当前命名空间尽可能的与主节点同步。
- JournalNode Cluster:用于主备节点间共享 editlog 日志文件的共享存储系统。负责存储 editlog 日志文件。基于 Paxos 算法实现,集群由 2N+1 个 JournalNode 组成,最多可以容忍 N 台 JournalNode 宕机。
- ZKFailoverController(ZKFC):以独立进程运行,每个 ZKFC 都监控自己负责的 NameNode,实现 NameNode 自动故障切换。
- ZooKeeper:为 ZKFC 进程实现自动故障转移提供统一协调服务。
- DataNode:实际存储文件 Block 块的地方,一个 Block 块包含两个文件:一个是数据本身,一个是元数据(数据块长度、块数据的校验和、时间戳)。
HA 主备故障切换流程:
HA 集群刚启动时,两个 NameNode 节点状态均为 Standby,之后两个 NameNode 节点启动 ZKFC 进程后会去 ZooKeeper 集群抢占分布式锁,成功获取分布式锁的 NameNode 会成为 Active NameNode。
当主 NameNode 宕机后,对应的 ZKFC 进程检测到 NameNode 状态,便向 ZooKeeper 发送删除锁的命令,锁删除后触发事件回调备用 NameNode 上的 ZKFC,ZKFC 得到消息后先去 ZooKeeper 争夺创建锁,锁创建完成后会检测原先的主 NameNode 是否真的挂掉,挂掉则升级备用 NameNode 为主节点,没挂掉则将原先的主节点降级为备用节点。
Block、Packet 及 Chunk 概念
- Block:HDFS 上的文件都是分块存储的,即把一个文件物理划分为一个 Block 块存储。Hadoop 2.X/3.X 默认块大小为 128M,1.X 为 64M。
- Packet:是 Client 端向 DataNode 或 DataNode 的 Pipeline 之间传输数据的基本单位,默认 64KB。
- Chunk:是最小的单位,它是 Client 向 DataNode 或 DataNode Pipeline 之间进行数据校验的基本单位,默认 512 Byte,因为用作校验,所以每个 Chunk 需要带有 4 Byte 的校验位,实际上每个 Chunk 写入 Packet 的大小为 516 Byte。
HDFS 读流程
以从 HDFS 读取一个文件为例,其读取流程如下图所示:

- 打开文件:客户端调用
DistributedFileSystem.open()方法打开文件,返回HdfsDataInputStream对象。 - 从 NameNode 获取 DataNode 地址:调用
getBlockLocations()方法向 NameNode 获取组成文件的 block 位置信息。 - 连接 DataNode 读取数据块:通过
DFSInputStream.read()方法,连接到离客户端最近的 DataNode 读取 Block 块,数据以 Packet 为单位传输。 - 关闭流:直到所有文件读取完成,调用
close()方法关闭输入流。
HDFS 写流程
HDFS 写流程涉及的方法比较多,过程也比较复杂。

- 在 NameNode 创建文件:Client 调用
DistributedFileSystem.create()方法,NameNode 确认文件是否存在及权限后,返回DFSOutputStream对象。 - 建立数据流 Pipeline 管道:通过
addBlock()申请新的空数据块,返回LocatedBlock对象保存可写入的 DataNode 信息,默认三个 DataNode 组成 pipeline。 - 通过数据流管道写数据:数据被切分成多个 Packet,放到
dataQueue中,DataStreamer线程发送到 pipeline 中的第一个 DataNode,依次传递。发送完后移入ackQueue等待确认。 - 关闭流并提交文件:调用
close()关闭输出流,通知 NameNode 提交文件中的所有数据块。
MapReduce
(MapReduce 相关详细内容请参考后续更新)
Yarn
(Yarn 资源调度相关详细内容请参考后续更新)
Spark
Spark 是 UC 伯克利 AMP 实验室开发的快速通用的大数据计算引擎。相比 MapReduce,Spark 主要有三个特性:
- RDD 的编程模型更简单:RDD(Resilient Distributed Dataset)提供了丰富的算子,编程更加简洁。
- DAG 切分的多阶段计算过程更快速:Spark 通过 DAG 对计算过程进行优化,减少了不必要的 shuffle。
- 使用内存存储中间计算结果更高效:Spark 优先使用内存存储数据,大大提高了计算速度。
这三个特性使得 Spark 相对 Hadoop MapReduce 可以有更快的执行速度,以及更简单的编程实现。
流式计算
Storm
Storm 是一个比较早期的大数据流计算框架。开发者只需定义好处理流程(Topology)和每一个节点的处理逻辑(Spout/Bolt),代码部署到 Storm 上后,就能按照预定义的处理流程执行。
其实大数据实时处理的需求早已有之,最早的时候,我们用消息队列实现大数据实时处理,过程如下:

Storm 也是主从架构:

- Nimbus:集群的 Master,负责集群管理、任务分配。
- Supervisor:Slave,真正完成计算的地方。每个 supervisor 启动多个 worker 进程,每个 worker 上运行多个 task(spout 或者 bolt)。
- Supervisor 和 Nimbus 通过 ZooKeeper 完成任务分配、心跳检测等操作。
Spark Streaming
Spark Streaming 巧妙地利用了 Spark 的分片和快速计算的特性,将实时传输进来的数据按照时间进行分段,把一段时间传输进来的数据合并在一起,当作一批数据,再交给 Spark 去处理。
如果时间段分得足够小,每一段的数据量就会比较小,再加上 Spark 引擎的处理速度又足够快,这样看起来好像数据是被实时处理的一样。
val ssc = new StreamingContext(conf, Seconds(1))
Flink
Flink 则相反,一开始就是按照流处理计算去设计的。当把从文件系统(HDFS)中读入的数据也当做数据流看待,它就变成了批处理系统。
Flink 的架构和 Hadoop/Yarn 类似,JobManager 是 Flink 集群的管理者,TaskManager 负责执行具体的计算任务。
Flink 对流处理的支持更加完善,它可以对数据流执行 window 操作:
.timeWindow(Time.seconds(10))
大数据技术全景
从下面这张图来看大数据技术的分类,可以分为存储、计算、资源管理三大类:

存储
- HDFS:最基本的存储技术,各种渠道得到的数据统统存储到 HDFS 上。
- HBase:NoSQL 类非关系数据库,底层存储用到 HDFS,可存储比 MySQL 多得多的数据量。
计算
- 批处理:MapReduce、Spark、Hive、Spark SQL
- 流处理:Storm、Spark Streaming、Flink
资源管理
- Yarn:管理服务器集群的计算资源,各种大数据计算引擎都可以通过 Yarn 进行资源分配。
通常这些技术部署在同一个集群中,某台服务器可能同时运行 HDFS 的 DataNode 进程和 Yarn 的 NodeManager,而 MapReduce、Spark、Storm、Flink 等计算引擎则通过 Yarn 的调度运行在容器(container)里面。
注:本文为大数据知识体系入门概述,各技术的详细原理和实战将在后续文章中展开。