区块笔记 BLOCK NOTES

Selected Topics in Big Data and Smart Society · COMP8122 · 澳门理工大学 · 罗吴蔓

切、算、并。

一份大数据课的视觉笔记。509 页讲义只讲一件事:切小块、并行算、合并回来。

  • 绿,静止的数据
  • 粉,正在移动的数据
  • 黑,排程与控制
翻开第一版

509 页,九份幻灯片;一格一页,滑过去看章名。

导论 1–9 · chap1 10–72 · chap2 73–140 · chap3 141–162 · chap4a 163–190 · chap4b 191–296 · chap5a 297–364 · chap5b 365–414 · chap6a 415–509

Plate I

复制机

chap3 · p.141–162

等距剖面图:一卷三层的数据纸送入压床,每一次冲程切出一块三联的区块,三条护栏输送带把它们分别送进三个机架;旁边的控制台有三盏灯。
一个文件,切成 128MB 的区块;每个区块切三份,一份留在写入端,两份跨到另一个机架。压床是写入路径,输送带是网络,三个机架是 DataNode。 这台机器只解释一件事:副本不是备份,是同一块数据的三个位置。

大不是体积,是失衡

chap1 · p.10–72

数据长得比摩尔定律快,机器只能靠便宜堆数量。当一千台机器平均每天都坏一台,「不会坏」就不再是设计前提。

128 MB一个 HDFS 区块
25 PBLHC 一年
60 PBFacebook 一座集群
1 ZB这把尺的顶端
尺度。每一格是一千倍,长度是对数。四张纸条是讲义里真的出现过的数字。

讲义把这件事拆成四个 V:Volume、Variety、Velocity、Veracity。量只是第一个。

上海五万辆计程车,每 15 秒回报一次位置,一天就是 2.88 亿个点。这些点不是为了被储存而存在,是为了被算。

上海计程车 GPS 轨迹叠在地图上的画面,黄色线条是一辆车一天的移动路径
Plate II · 上海计程车轨迹。5 万辆,每 15 秒一个点;黄线是一辆车一天的路径。

把文件切开,撒在会坏的机器上

chap3 · p.141–162

128MB 一块,默认三份。第一份靠近写入端,第二份跨到别的机架,第三份回到第二个机架的另一台。NameNode 只记位置,DataNode 才真的存数据。

切块器

拖文件大小,看它变成几个区块、几份副本。

8,192块
24,576份副本
3份最少可用
数据完整目前状态

三份副本落在两个机架:第一份在写入端,第二、三份跨到另一个机架的另一台。

没有脚本也能读:1 TB 的文件 = 8,192 个 128MB 区块 × 3 份副本 = 24,576 份复本;单一台机器或单一机架损坏,数据仍然可读。

粉红色的副本代表正在搬动或正在补回,绿色的代表静止在磁盘上。

这不是备份,是概率。一台机器三年下线一次,一千台就是每天一台;副本让「坏掉」变成例行工作,而不是事故。

Hadoop 的原则是反过来的:不要叫机器变得更可靠,叫软体接受它会坏。所以它把程序搬到数据旁边,不是把数据搬到程序旁边。HDFS 管储存、YARN 管资源;两层分开,储存与运算才能各自长大。

机房走道,两侧是黑色机柜,地板是穿孔砖
Plate III · 一座运行中的机房。这门课假设你买的是这种便宜货,而且数量很多。

同一个键,永远去同一个 reducer

chap2 · p.73–140

map 逐行产生 (键, 值);partitioner 用键的哈希决定它去哪;同一个键的所有值在 reducer 碰头、排序、合并。

洗牌机器

点任何一个词,看它被送到哪一个 reducer。切换 reducer 数量,分布会变。

没有脚本也能读:四个文件里 Alice 出现 4 次、Bob 5 次、Carol 3 次;同一个词的全部出现次数一定被送到同一个 reducer。

四份文件;reducer 的数量可以切换。虚线是网络,同一个键只会有一条路。

副作用很有用:每个 reducer 收到的键是排好序的,所以 sort 只要一个 range partitioner,join 只要把两边的数据打上 LEFT / RIGHT 标签送进同一个 reducer。后来的 Hive 与 Pig 把 SQL 叠回 MapReduce,HadoopDB 则把两者缝在一起。

RDBMS 假设查询会跑完,MapReduce 假设它一定会掉。Google 自己的数字是每个 job 平均失败 1.2 次,所以它把「重跑」写进模型里,而不是写在维运手册里。

并行 RDBMS 与 MapReduce:不是谁比较好,是假设不同
差异并行 RDBMSMapReduce
schema必须有;换来逻辑独立与数据共享不需要;结构由写程序的人自己管
表达方式SQL 加 UDF,宣告式程序语言,自己排数据流
排程编译期,有优化器执行期,没有全域优化
数据流push,中间结果不落地pull,中间结果落地
粒度查询级储存区块级
Yahoo! 1TB 排序任务的时间轴图表:红、蓝、紫三带分别是 shuffle、map 与 reduce 任务数
Plate IV · Yahoo! 的 1TB 排序。红的是 shuffle,蓝的是 map,紫的是 reduce;曲线的形状就是这门课的形状。

少搬一次,就快一百倍

chap4a–b · p.163–296

RDD 不可变,所以坏掉的 partition 可以从 lineage 重算;narrow dependency 可以叠成同一个阶段,wide dependency 一定要洗牌。快不是因为它更聪明,是因为中间结果留在内存。

DAG 分阶段

按「执行」看 Spark 真的跑了什么;按「弄坏一个分区」看它怎么重算。

没有脚本也能读:map 与 filter 之间是 narrow dependency,可以合成一个阶段;reduceByKey 之前是 wide dependency,必须洗牌,也是阶段的边界。

两个阶段,一次洗牌。绿色的节点是留在内存的数据,粉色是跨网络搬动的量。

没有 action,上面那条链什么都不做。lazy 不是省事,是让 Spark 有机会把 map 与 filter 排进同一次扫描;而 take(10) 算到第十个就停,后面的数据连读都不读。分区器决定一列去哪里:reduceByKey 用哈希,sortByKey 用范围。

上面那些 map、filter、reduce,语法不是 Spark 发明的,是 Scala 集合搬上集群。不可变的 val 与高阶函数,让「重算」变成安全的默认。

val lines = sc.textFile("hdfs://log.txt")
val errs  = lines.filter(_.contains("Exception"))
val byHost = errs
  .map(l => (hostOf(l), 1))
  .reduceByKey(_ + _)
byHost.take(10)          // action:到这里才真的算
Spark 讲义原图:一个 job 拆成三个 stage,每个 stage 有多个 task,分配给多个 executor
Plate V · 一个 job 拆成 stage,task 落到 executor。切在哪里,由 wide dependency 决定。

先砍掉不可能,再数剩下的

chap5a–b · p.297–414

支持度是「出现几次」,置信度是「出现之后,真的会买的比率」。单调性让候选集一层层变小:Apriori 拿多次扫描换内存,FP-tree 拿一次扫描换一棵树。

两个算法,同一张表

五笔交易,支持度门槛 3;切到 FP-tree,看它怎样不扫第二次。

五笔交易
TIDABCDE
t110010
t211011
t301100
t411111
t501101
1 / 5

没有脚本也能读:支持度 3 之下,频繁 2-项集只有 {A,D}、{B,C}、{B,E};{B,C}→E 的支持度只有 2,所以它连候选都不是。

讲义用 {B,C}→E 讲置信度 66.7%;套上支持度 3,它过不了第一关。两个门槛各自把关,这不是教材矛盾。

找所有频繁项集本身是 NP-hard,所以后面每个算法都在换:换扫描次数、换内存、或换一点正确性。PCY 用哈希桶先滤掉不可能的配对;SON 把数据切块各跑一次,再用单调性取并集;Toivonen 用负边界赌一把,赌输就换一组样本重来。

维度一多,邻居就消失

chap6a · p.415–509

在很高的维度里,任两点的距离几乎一样长。与其先降维再聚类,不如直接问:哪几个维度的组合,本身就有密集的格子?

子空间,示意

切换看哪些维度。两维一起看时,族群会消失。

密集单位 0 / 36。每格密度最高 4.2%,离 20% 很远。

这是示意图,不是讲义数据。密集单位 = 落在此格的点超过 20%;相连的密集单位才是一个集群。

KL 转换

讲义的四个点。按「转轴」,看它们投影到哪里。

按「转轴」,把坐标系转到特征向量的方向。

(3,3)、(−1,−1)、(0,2)、(2,0)。协方差矩阵的特征值 4 与 1;投到 λ=1 的方向,得 0、−1.41、0、1.41。

没有脚本也能读:密集单位是落在同一格超过 20% 的数据;两维一起看时没有密集单位,只看 Age 时 A3 与 A5 各 25%。KL 部分:四个点投影到 λ=1 的方向得 0、0、−1.41、1.41。

CLIQUE 用 apriori 找密集单位,再把相连的格子并成集群;熵方法把「好聚类」写成 H(X) < ω(八匹马等概率是 3 bit,确定是 0 bit);KL 转换把坐标轴转到方差的方向,最小特征值那条,常常就是答案。

整条路的帐单

本页的计算

把一份 1 TB 的数据推过三个闸:写入、洗牌、留在内存。帐单只记一件事:总共搬动了多少 bytes。

到目前为止,共搬动

0 B

0× 输入

写入 HDFS
洗牌
留在内存

写入磁盘0 B

跨网络0 B

再写一次(没有 cache)0 B

没有脚本也能读:1 TB 输入,三份副本 = 写 3 TB、跨网络 2 TB;洗牌量取决于 combiner;没有 cache 时中间结果要再落地一次。

这是本页的估算模型,不是讲义原文:假设 1 TB 的 word count,map 输出约 2.4 TB;combiner 把它压到几 GB。真实比例取决于你的键有多重复。推进顺序会改变帐单,因为帐单是路径的函数。