通过弄清数据的来龙去脉,来理清复杂的系统架构,是本书的宗旨。

三种系统:

  • 在线服务(或称在线系统)

    服务等待客户端请求或指令的到达。当收到请求或指令时,服务试图尽快的处理它,并发挥响应。响应时间和可用性是衡量标准。

  • 批处理(或称离线系统)

    接收大量的输入数据,运行一个作业来处理数据,并产生输出数据。衡量标准是吞吐量。

  • 流处理(或称近实时系统)

    流处理介于在线与离线之间。与批处理类似,流处理处理输入并产生输出。但是,流处理作业在事件发生不久就可对事件进行处理。

linux命令是单机版的批处理,map-reduce是分布式的批处理。unix工具使用stdin和stdout作为输入输出,MapReduce作业使用HDFS(Hadoop Distributed File System)分布式文件系统读写文件。

除HDFS外,还有GlusterFS和Quantcast File System(QFS)。注入Amazon S3,Azure Blob存储和Open Swift对象存储服务也有相似之处。

HDFS在每台机器上都有一个守护进程,并开放一个网络服务以便其他节点访问存储在该机器上的文件。名为NameNode的中央服务器会跟踪哪个文件存储在哪台机器上。

批处理的输出:

  • 生成搜索索引。尽管如今Google不再使用MapReduce构建索引,但是MapReduce仍是构建Lucene和Solr的好方法。
  • 构建机器学习系统。比如分类器(如垃圾邮件过滤,异常检测,图像识别)和推荐系统(可能认识的人,可能感兴趣的产品)。

MapReduce虽然在模型抽象上比较简单,但是使用起来却很不方便。例如,你需要从头开始实现全部join算法。

MapReduce在物化(将中间状态写入文件的过程)的过程中存在如下问题:

  • 前面的作业完成之后,后面的作业才能开始。
  • Mapper通常是冗余的。它们通常是读取前一个reducer写入的文件,并为下一个分区和排序阶段做准备。通常可以和reducer放在一起。
  • 中间状态会复制到多个节点。

为了解决这些问题,Spark、Tez和Flink等数据流引擎应运而生。它们有一个共同点:将整个工作流作为一个作业完成,而不是把它分解为独立的子作业。同时也不严格区分map和reduce角色,而是以函数运算符进行组合。优点:

  • 排序等昂贵的操作只在需要的地方进行,而不是在map和reduce之间默认发生。
  • 没有不必要的map,可以合并到前一个reduce中。
  • 所有join和数据依赖都是明确声明的,调度器知道哪些是必需的,因此可以进行本地优化。比如将使用某些数据的任务放在生成数据的机器上,避免网络复制。
  • 将中间状态保存在内存或本地磁盘就够了,比写入HDFS更省IO。
  • 运算符在输入准备就绪后立即开始,不用等待上一阶段全部完成。
  • MapReduce每个任务启动一个JVM,现在可以重用JVM。

Spark、Flink和Tez为了避免写入中间状态,同时失败了能够重新开始计算,必须在框架层追踪给定数据是如何计算的,使用了哪个输入分区以及应用了哪个运算符。Spark使用弹性分布式数据集(Resilient Distributed Dataset,RDD)来追踪数据的祖先,Flink对运算符状态建立检查点。