《Designing Data Intensive Applications》读书笔记 - 批处理

Unix 工具批处理

这一章先以 Unix 命令处理日志分析为例,介绍 Unix 设计的哲学,之后介绍 MapReduce 时会比较它们的相似之处。

下面这个命令是一个简单的日志分析,统计访问量最多的前 5 个 URL:

1
2
3
4
5
6
cat /var/log/nginx/access.log |
awk '{print $7}' |
sort |
uniq -c |
sort -r -n |
head -n 5

同时与 Ruby 脚本的实现代码对比,Unix 链式管道更为简洁。另外一个大的不同时,Ruby 脚本使用了一个哈希表,而 Unix 命令使用了排序。
如果数据量较小,所有的数据都能够放入内存,哈希表的性能更好。如果数据量非常大,不能完全放入内容,排序的方法就更合适。
sort 命令可以自动处理大量数据,超出内存会使用磁盘并且利用多核并行排序。

Unix 哲学:每个程序只做一件事,但是做的很好;程序之间通过管道连接;统一的文本接口;逻辑和 I/O 分离。

MapReduce 和分布式文件系统

MapReduce 有点像 Unix 工具,只不过可能分布在上千台机器上。每个 MapReduce 任务就像单个 Unix 命令,接收一个或多个输入,产生一个或多个输出,通常不会修改输入。

MapReduce 读写分布式文件系统上的文件,Hadoop 的 MapReduce 文件系统是 HDFS,基于无共享原则。 每台机器上运行一个守护进程,对外暴露网络服务,允许其它节点访问存储其上的文件。一个中心化的 NameNode 记录文件的位置。
考虑故障,每个文件会有多个副本。

MapReduce 作业执行

以上面的日志分析为例,一个 MapReduce 作业步骤:

  1. 读取输入文件,将其分成记录,每个记录对应一行日志。
  2. 调用 mapper 函数提取记录中的 URL 作为 key, value 为空
  3. 排序所有的键值对
  4. 调用 reducer 函数处理排序后的键值对

分布式执行
和 Unix 命令行最大的不同是 MapReduce 可以在多个节点上并行执行。map 任务的数量通常由输入文件块的数量决定,而 reduce 任务的数量由用户指定。框架会确保相同 key 的记录会被发送到同一个 reduce 任务。
键值对需要进行排序,但是数据量可能非常大,无法在单台机器上排序,需要分阶段排序。首先,map 任务会对输出进行排序,基于 key 的哈希将输出分为多个分区(对应 reducer),每个分区写入到单独的文件中。
在 mapper 完成任务之后, reducer 开始下载排序后的文件并进行合并同时保留了原来的顺序。

MapReduce 工作流
单个 MapReduce 可以解决的问题有限,像上面的日志分析例子,单个任务只能决定每个 URL 的访问次数,如果需要找到最热门的 URL,需要另外一个 MapReduce 任务。所以,将多个 MapReduce 任务组合成一个工作流是很常见的。
有点像 Unix 中的管道了。

Reduce-Side Join and Grouping

数据库的 join 操作非常常见,比如一个数据集是用户信息,另一个数据集是用户的日志,需要将两个数据集合并到一起。

Sort-merge joins
两个 mapper 任务,一个处理用户信息,一个处理日志,输出的键值对都是用户 ID 和信息。在 reduce 阶段,将两个数据集合并到一起,这个过程叫做 sort-merge join。

mapper 就像发送信息到 reducer,而 key 就像地址。

处理倾斜
如果存在热点,比如一个用户有大量的日志,这个用户的数据会集中在一个 reducer 上,就会导致这个 reducer 的负载很高。
一种方法是,将热点分散到多个 reducer 上。 Hive 使用的是另一种方法,需要先标识出热点,然后在执行 join 时,使用 map-side join。

Map-Side Join

Broadcast hash join
简单来说就是如果用户数据量比较小,直接在 map 阶段将用户数据加载到内存中,然后在 map 阶段就可以进行 join 操作。

Partitioned hash join
如果数据量比较大,不能放入内存,可以将用户数据分区,每个分区对应一个 reducer,然后在 map 阶段将用户数据发送到对应的 reducer 上。

应用

一是建立索引,二是机器学习分类或推荐系统。

与分布式数据库的比较

可以说 MapReduce 更为灵活,存储可以是任意格式数据。另外,MapReduce 是针对频繁故障设计,倒不是硬件太不可靠,而是需要任意终止任务。

MapReduce 之上

MapReduce 很容易理解,但是使用起来非常麻烦,所以有了很多的抽象实现。

中间状态的物化

MapReduce 任务之间的数据传递是通过文件系统,每个任务都会将输出写入到文件系统,下一个任务再读取。和开头的 Unix 例子相比,这样的不足:

  1. 作业只有在所有先行作业完成之后才能开始
  2. 不必要的 Mapper
  3. 存储这些中间状态意味着在节点之间复制

保存这些中间状态的一个好处是容错,如果一个任务失败,可以重新执行。 Flink 和 Spark 避免将中间状态写入文件系统,在机器出现故障时,需要重新计算,这就需要框架追踪数据时如何计算的。

高级 API

高级语言像 Pig 和 Hive,可以将 SQL 转换为 MapReduce 任务。这些语言提供了更高级的抽象,比如 join 操作,分组,过滤等。

声明式查询语言
查询优化器可以利用列式存储只读取需要的列。

总结

这一章感觉内容并不深,尤其是已经对 MapReduce 有所了解的情况。最有意思或许是将其与 Unix 命令行做比较,让人更有启发。

《Designing Data Intensive Applications》读书笔记 - 数据库复制

这一章讲数据库复制(Replication),目标很简单就是保存数据副本在多个机器上,但是实现却没那么容易。首先需要数据复制的几个原因:

  1. 数据中心地理上更靠近用户
  2. 增强可用性,即便部分服务器节点失败,整个系统依然可用
  3. 提高读取的吞吐量
阅读更多

《The DynamoDB Book》读书笔记

同事推荐的一本书,只有英文电子版。作者是Alex DeBrie,之前介绍过,是单表设计的推崇者。

这本书前面部分几个章节介绍 DynamoDB 的基本概念,后面部分是一些实际的设计案例。

阅读更多

《从程序员到架构师》笔记

书名有点大,其实书中更偏向于一些架构场景介绍,书中讲解了作者经历过的十几次架构设计,介绍了场景,技术选择以及相关的权衡。书很快就能读完,对我个人来说多少有些收获。

阅读更多

机器学习笔记

在读吴军的《智能时代》和《数学之美》时常常被技术的力量给震撼到,尤其是其中现实的例子特别具有说服力,未来的发展或许由人工智能驱动。当然在深度方面,自学肯定不如专科博士生,不过未来人工智能或许就像现在的编程一样成为一种基础工具。今年的计划之一就是学习人工智能,因为离开学习太久,学习理论的东西不如从前。这一篇指南 如何用3个月零基础入门「机器学习」 提供的建议非常受用。目前在看 吴恩达的 《机器学习》视频,已完成监督学习部分,这儿做一个阶段性复习。

阅读更多

《浪潮之巅》笔记

之前自己对畅销书一直有成见,这本书一直没在自己的读书清单上。最近因为内人的强烈推荐,一口气把这本书读完了。先说说感受:这本书的信息量很大,囊括了信息产业革命之后所有知名技术公司的兴衰。好多年前,听朋友在谈这本书的:回看过去,全球技术的变革就像浪潮一样。当时不以为然:这都能算是一个概念,这和蓝平长二畅销书一个套路嘛。如果当时朋友这么推荐的话:过去几十年信息产业公司巨头的兴起和衰落,其实有着深层次的原因,通过这本书,可以了解未来互联网格局的变化;当时或许就不会错过这本书。虽说现在读这本书没有那种恍然大悟或者相见恨晚的感觉,但也不得不说如果早几年读这本书或许自己会有不一样的选择。再说说这本书不好的一面:过分的强调基因的作用,给人一种宣扬宿命论的感觉;另外作者的主观色彩比较浓厚,毕竟这本书就是他个人的视角(可以想象一下,如果这本书出自一个做研究的学者,对每个公司可能会有非常慎重公平的评价)。

阅读更多

CSAPP 笔记

《深入理解计算机系统》这本书是 CS 的经典书目。翻看之处,思绪总是回到学校,感慨良多,也悔恨当年在学校没有好好学艺,更没有穷根究底的思考。整体来看的话,这本书更像是导论,所讲内容部分属于计算机组成原理,部分是操作系统,还有部分汇编。单从知识点上,这本书讲的并不全面,不过还是有不少收获。

阅读更多