41

MapReduce 进阶

Embed Size (px)

DESCRIPTION

Cloud Computing. MapReduce 进阶. 主要内容( 6 学时). 简介. MPI(Message Passing Interface) 等并行编程方法 缺少对高层并行编程模型和统一计算框架的支持,需要程序员处理许多底层细节, 为此 MapReduce 在三个层面上做了系统而巧妙的设计构思。 在大数据处理的基本方法上,对相互计算依赖不大的数据采取“分而治之”的处理策略。 借鉴了 Lisp 语言中的思想,用 Map 和 Reduce 两个函数提供了高层的并行编程抽象模型和接口。 - PowerPoint PPT Presentation

Citation preview

Page 1: MapReduce 进阶
Page 2: MapReduce 进阶
Page 3: MapReduce 进阶

MPI(Message Passing Interface) 等并行编程方法缺少对高层并行编程模型和统一计算框架的支持,需要程序员处理许多底层细节, 为此 MapReduce 在三个层面上做了系统而巧妙的设计构思。◦ 在大数据处理的基本方法上,对相互计算依赖不大的数据采取“分而治之”的处理策略。

◦ 借鉴了 Lisp 语言中的思想,用 Map 和 Reduce 两个函数提供了高层的并行编程抽象模型和接口。

◦ 对于诸多的底层实现和处理细节 MapReduce 提供了一个统一的计算框架,大大减轻了程序员在编程是的负担。

Page 4: MapReduce 进阶

把小的键值对合并成大的键值对◦ Map 计算过程中所产生的中间结果键值对需要通过网络传输给

Reduce 节点,大规模的键值对可能会大幅增大网络通信开销,并且降低程序执行速度,为此开采用一个基本的优化方法,即把大量小的键值对合并为较大的键值对。

◦ 例如在单词同现矩阵计算中,单词 a可能会与多个其他单词共同出现,因而一个 Map 可能会产生很多个单词 a与其他单词的键值对,如下:

Page 5: MapReduce 进阶

◦ <a, b> 1◦ <a, c> 3 a {b:1,c:3, d:5, e:8,

f:4} ◦ <a, d> 4 ◦ <a, e> 8◦ <a, f> 4

Page 6: MapReduce 进阶

Map 计算过程中,系统自动按照 Map 的输出键进行排序,因此进入 Reduce 的键值对都是按照 key 值排序的,但有时希望 value 也按一定规则排序。

方法 1 :在 Reduce 过程中对 {value} 列表中的值进行 本地排序,但当 {value} 列表数据量巨大时

必须使用复杂的外排算法,会很耗时。 方法 2 :将 value 中需要排序的部分加入到 key 中, 形成复合键,这样能利用 MapReduce系统

的排序功能自动完成排序。

Page 7: MapReduce 进阶

Hadoop 内置的数据类型 BooleanWritable :标准布尔型数值 ByteWritable :单字节数值 DoubleWritable :双字节数 FloatWritable :浮点数 IntWritable :整型数 LongWritable :长整型数 Text :使用 UTF8 格式存储的文本 NullWritable :当 <key, value> 中的 key 或 value 为空时使用

Page 8: MapReduce 进阶

自定义数据类型的实现◦首先实现 Writable 接口,以便该数据能被序列化后完成网络传输或文件输入 /输出;

◦其次,如果该数据需要作为 key 使用,或者要比较数值大小时,则需要实现 WritableComparable 接口。

◦例如将一个三维坐标 P(x,y,z) 定制为一个数据类型 pubic class Point3D implements Writable<Point3D> { private float x,y,z; public void readFields(DataInput in) throws IOException {……}

public void write(DataOutput out) throws IOException {……} }

Page 9: MapReduce 进阶

如果 Point3D 还需要作为主键值使用,或者需要比较大小时,还应该实现 WritableComparable接口 pubic class Point3D implements WritableComparable<Point3D>

{ private float x,y,z; public void readFields(DataInput in) throws IOException {……}

public void write(DataOutput out) throws IOException {……} }

Page 10: MapReduce 进阶

Hadoop 内置数据输入格式和 RecordReader◦ TextInputFormat :是系统默认的数据输入格式,可以文本文件分块逐行读入,读入一行时,所产生的 kye 为当前行在整个文件中的字节偏移位置,而 value 就是行内容。

◦ KeyValueInputFormat :是另一个常用的数据输入格式,可将一个安照 <key, value> 格式逐行存放的文件逐行读出,并自动解析成相应的 key 和 value 。

Page 11: MapReduce 进阶

RecordReader :对于一个数据输入格式,都需要有一个对应的 RecordReader 。 RecordReader 主要用于将爱那个一个文件中的数据记录分拆成具体的键值对,传给 Map 函数。例如: TextInputFormat 的默认RecordReader 为 Line RecordReader , KeyValueInputFormat 的默认 RecordReader 为KeyValueLine RecordReader 。

除此之外,系统还提供很多输入格式,例如: AutoInputFormat, CombineFileInputForma 等

Page 12: MapReduce 进阶

假设需要定制 FileNameLocInputFormat 与 FileNameLocRecordReader, 以便产生FileName@LineOffset 主键值,则可定制如下代码pubic class FileNameLocInputFormat extends FileInputFormat<Text, Text>{

@Override public RecordReader<Text, Text> createRecordReader(InputSplit split,

TaskAttemptContext context){ FileNameLocRecordReader fnrr = new

FileNameLocRecordRader(); fnrr.initialize(split, context);

………… }

}

Page 13: MapReduce 进阶

pubic class FileNameLocRecordReader extends RecordReader<Text, Text>{

String FileName;LineRecordReader lrr = new LineRecordReader(); @Overridepublic Text getCurrnetKey()

{ return new Text(“+FileName+”@”+lrr.getCurrentKey()+”);

}@Overridepublic void initialize(InputSplit arg0, TaskAttemptContext arg1){

……………………….}

}

Page 14: MapReduce 进阶

Hadoop 内置的数据输出格式与 RecordWriter◦ Hadoop 提供了丰富的内置数据输出格式。最常用的是

TextOutputFormat ,也是系统默认的输出格式,可以将计算结果以 key + \t +value 的形式逐行输入到文件中。

◦数据输出格式也提供一个对应的 RecordWriter ,以便系统明确输出结果写到文件的具体格式。 TextOutputFormat 的默认 RecordWriter 是LineRecordWriter 。

Page 15: MapReduce 进阶

默认情况下, MapReduce 将产生包含一至多个文件的单个输出数据文件集合。但有时候需要输出多个文件集合。比如,在处理专利数据是,希望根据不同国家,将每个国家的专利数据记录输出到不同国家的文件目录中。

Hadoop 提供了 MultipleOutputFormat 类帮助完成这一处理功能。在 Reduce 进行数据输出前,需要定制MultioleOutputFormat 的一个子类,实现其中的一个重要方法

protected String generateFileNameForKeyValue(K key, V value, String name)

通过该放过,程序可以根据输入的主键产生并返回一个所期望的输出数据文 件名和路径

Page 16: MapReduce 进阶

Hadoop MapReduce 提供了默认的 Partition来完成 Map 节点数据的中间结果向 Reduce 节点的分区处理。大多数情况下,应用程序仅使用默认的HashPartitiner 即可满足计算要求,但有时候,数据可能不会被均匀的分配到每个 Reduce 上,或者不符合应用程序的分区要求,这时,就需要定制自己的 Partitioner 。

Page 17: MapReduce 进阶

定制一个 Partitioner 大致方法如下,继承HashPartitioner ,并重载 getPartition() 方法

class NewPartitioner extends HashPartitioner<K, V> {

//override the methodgetPartition(K key, V value, int numReduceTasks){………………….}

}

Page 18: MapReduce 进阶

用户也可以根据需要定制自己的 Combiner ,以减少 Map阶段输出中间结果的数量,降低数据的网络传输开销。

class NewCombiner extends Reducer<Text, IntWritable, Text, IntWritable>

{public void reduce(Text key, Iterable<IntWritable> values, Context

context) throwd IOException, InterruptedException{………………………}

}

Page 19: MapReduce 进阶

一些复杂任务难以用一趟MapReduce 处理完成,需要将其拆分为多趟简单的 MapReduce子任务进行处理。

本节将介绍多种不同形式的组合 MapReduce任务,包括迭代方法, 顺序组合方法, 具有以来关系的组合方法,以及链式方法

Page 20: MapReduce 进阶

当使用 MapReduce 进行这样的问题求解时,运行一趟MapReduce 过程将无法完成整个求解结果,因此,需要采用迭代法方循环运行该 MapReduce过程,直到达到一个逼近结果。

例如页面排序算法 PageRank 就是这一类需要用循环迭代MapReduce 计算进行求解的问题。

Page 21: MapReduce 进阶

多个 MapReduce子任务可以注意手工执行,但更方便的做法是将这些子任务串起来,前面的输出作为后面的输入。例如

mapreduce1 mapreduce2 mapreduce3

Page 22: MapReduce 进阶

例如,一个 MapReduce 作业有 x, y, z 三个子任务,它们的依赖关系如下:

Job

Jobz

JobyJobx

Page 23: MapReduce 进阶

则他们的配置程序如下 //配置 jobx

Configuration jobxconf = …. // 设置 JobControl Job jobx = … JobCOntrol jc = new JobControl(); //配置 joby jc.addJob(jobx); Configuration jobyconf = …. jc.addJob(joby); Job joby = … jc.addJob(jobz); //配置 jobz jc.run(); Configuration jobzconf = …. Job jobz = … // 设置依赖关系 jobz.addDependingJob(jobx); jobz.addDependingJob(joby);

Page 24: MapReduce 进阶

一个 MapReduce 作业可能会有一些前处理和后处理步骤,比如文档倒排索引处理前需要去除“停用词”,若将其设置为一个单独的 MapReduce 作业,则可能会影响核心作业的效率。

为此,一个较好的方法是自啊核心的 Map 和 Reduce 过程之外,把这些前后处理步骤实现为一些辅助的 Map 过程,将这些辅助过程与核心Map 和 Reduce 过程合并为一个链式 MapReduce任务。

Page 25: MapReduce 进阶

Hadoop 为此专门提供了链式 Mapper(ChainMapper) 和链式 Reducer(ChainReducer)来完成这种处理。 ChainMapper允许在单一 Map任务中添加和使用多个 Map子任务;而 ChainReducer允许在一个单一Reduce任务执行了 Reduce 处理后,继续使用多个Map子任务完成后续处理。

Page 26: MapReduce 进阶

一个 MapReduce任务可能需要访问多个数据集,在关系数据库中,这将是两个或多个表的连接(join) 。 Hadoop 系统没有关系数据库那样强大的连接处理功能,大多数时候需要程序员自己实现,本节介绍基于 DataJoin 类库实现 Reduce端连接的方法,用全局文件复制实现 Map端连接的方法,带Map端过滤的 Reduce端连接方法,以及MapRedude 数据连接方法的限制

Page 27: MapReduce 进阶

用 DataJoin 类库完成数据源连接的基本处理方法如下: 首先需要为不同数据源下的每个数据记录定义一个数据源标签 (Tag) 。

进一步,为了能准确的标识一个数据源下的每个数据记录并完成连接处理,需要为每个带连接的数据记录确定一个连接主键 (GroupKey)

最后, DataJoin 类库分别在 Map 和 Reduce阶段提供一个处理框架,并尽可能的帮助程序员完成一些处理工作,剩余部分必须自己完成。

Page 28: MapReduce 进阶

根据 GroupKey 进行分区

数据源 Customers1 ,王二, 025-1111-11112 ,张三, 021-2222-22223 ,李四, 025-3333-33334 ,孙五, 010-4444-4444

数据源Orders3 ,订单 1 , 90 , 2011.8.1 1 ,订单 2 , 130 , 2011.8.6

2 ,订单 3 , 220 , 2011.8.10 3 ,订单 4 , 160 , 2011.8.18

Map Map

Customers1 ,王二, 025-1111-1111

Customers2 ,张三, 021-2222-2222

Customers3 ,李四, 025-3333-3333

Customers4 ,孙五, 010-4444-4444

Orders3 ,订单 1 , 90 , 2011.8.1

Orders 1 ,订单 2 , 130 , 2011.8.6

Orders2 ,订单 3 , 220 , 2011.8.10

Orders3 ,订单 4 , 160 , 2011.8.18

1

2

3

4

3

1

2

3

Page 29: MapReduce 进阶

Reduce 节点接收到这些带标签的数据记录后, Reduce 过程将对不同数据源标签下具有相同GroupKey 的记录进行笛卡尔叉积,自动生成所有不同的叉积组合。然后对每一个叉积组合,由程序员实现一个 combine() 方法,将这些具有相同GroupKey 的记录进行适当的处理,以完成连接。过程如图:

Page 30: MapReduce 进阶

Customers

3 ,李四, 025-3333-3333

Orders3 ,订单 1 , 90 , 2011.8.1

Orders3 ,订单 4 , 160 , 2011.8.18

3

Reduce

Customers

3 ,李四, 025-3333-3333

Orders3 ,订单 1 , 90 , 2011.8.1

Customers

3 ,李四, 025-3333-3333

Orders3 ,订单 4 , 160 , 2011.8.18

Combine() Combine()

3,李四, 025-3333-3333 ,订单 1 , 90 ,2011.8.1

3,李四, 025-3333-3333 ,订单 4 , 160 ,2011.8.18

Page 31: MapReduce 进阶

前述用 DataJoin 类实现的 Reduce端连接方法中,连接操作直到 Reduce端才能进行,因为很多无效的连接组合数据在 Reduce阶段才能去除,所以大量的网络带宽被用来传输不用的数据,因此,这种方法效率不高。

当数据源的数据量较小时,能够放在单节点的内存时,可以使用称为“复制连接”的全局文件复制方法,把较小的数据源复制到每个 Map 节点上,然后在 Map阶段完成连接操作

Page 32: MapReduce 进阶

Hadoop 提供了一个 Distributed Cache机制用于将一个或多个文件分布复制到所有节点上。要利用此机制,需要涉及到以下两部分的设置

(1) Job 类中 public void addCacheFile(URI uri): 将一个文件存放到 Distributed Cache 文

件中 (2) Mapper 或 Reducer 的 context 类中 public Path[] getLocalCacheFiles(): 获取设置在 Distributed Cache 中的文件路径

Page 33: MapReduce 进阶

当较小的数据源文件也无法放入内存时,可采用以下办法:

(1) 可以首先过滤较小数据源文件中的记录,只保 留需要进行连接的记录 (2) 可以将较小数据源文件分割为能 n个小文件,其 中每个小文件都能放入内存处理,然后分别对

这 n个小文件用全局文件复制方法进行与较大源

文件的连接,最后把结果合并起来

Page 34: MapReduce 进阶

如果过滤后数据仍然无法放在内存中处理,可采用带 Map端过滤的 Reduce端连接处理。

具体过程为在 Map端先生成一个仅包含连接主键的过滤文件,由于这个文件的数据量大大降低,则可将这个文件存放在 Distributed Cache 文件中,然后在 Map端过滤掉主键不在这个列表中的所有记录,然后再实现正常的 Reduce端连接

Page 35: MapReduce 进阶

为了能让用户灵活设置某些作业参数,一个MapReduce 计算任务可能需要在执行时从命令行输入这些作业参数,并将这个参数传递给各个节点

Configuration 类为此专门提供了用于保存和获取属性的方法例如:

public void set(String name, String value);// 设置字符串属性 public void get(String name, String defaultValue) // 读取字符串属性 // 将一个参数设置为 name属性 jobconf.set(“name”, args[0]); //然后可在 Mapper 或 Reducer 类的初始化方法 setup 中从 Configuration 对象读

出该属性值 jobconf.get(“name”, “”);

Page 36: MapReduce 进阶

这里同样也用到了 Distributed Cache机制。要利用此机制,需要涉及到以下两部分的设置

(1) Job 类中 public void addCacheFile(URI uri): 将一个文件存放到 Distributed

Cache 文件中 (2) Mapper 或 Reducer 的 context 类中 public Path[] getLocalCacheFiles(): 获取设置在 Distributed Cache

中的文件路径

Page 37: MapReduce 进阶

Hadoop 提供了相应的从关系数据库查询和读取数据的接口。◦ DBInputFormat :提供从数据库读取数据的格式◦ DBRecordReader :提供读取数据记录的接口

Hadoop查询和读取关系数据库的处理效率比较低因此 DBInputFormat仅适合读取小量的数据,对于读取大量的数据,可以用数据库中的工具将数据输出为文本文件,并上载到 HDFS 中处理。

Page 38: MapReduce 进阶

Hadoop 提供了相应的向关系数据库直接输出计算结果的编程接口。◦ DBOutputFormat :提供向数据库输出数据的格式◦ DBRecordWriter :提供向数据库写入数据记录的接口◦ DBConfiguration :提供数据库配置和创建连接的接口

//连接 DB 的静态方法Public static void configureDB(Job job, String driverClass, String dbUrl, String userName, String password)

Job 为当前准备执行的作业, driveClass 为数据库厂商提供的访问数据库的驱动程序

dbUrl 为运行数据库主机的地址, userName 和 password 为访问数据库的用户名与密码

Page 39: MapReduce 进阶

数据库连接完成后,即可完成从MapReduce 程序向关系数据库写入数据的操作, DBOutputFormat 提供了一个静态方法来指定需要写入的数据表和字段:

public static void setOutput(Job job, String tableName, String… fieldNames)

其中 tableName指定即将写入数据的表名,后续参数指定哪些字段数据将写入该表

Page 40: MapReduce 进阶

为了能完成向数据库中的写入操作,程序员还需要实现 DBWritable :

public class NewDBWritable implements Writable, DBWritable

{public void write(DataOutput out)

{……} ………. }

Page 41: MapReduce 进阶