边数据

边数据(side data)是作业所需的额外的只读数据,以辅助处理主数据集。所面临的挑战在于如何使所有map或reduce任务(这些任务散布在集群内部)都能够方便而高效地使用边数据。

利用Job来配置作业

Configuration类的各种setter方法能够方便地配置作业的任一键值对。如果仅需向任务传递少量元数据则非常有用。用户可以通过Context类的getConfiguration()方法获得配置信息。
一般情况下,基本类型足以应付元数据编码。但对于更复杂的对象,用户要么自己处理序列化工作(这需要实现一个对象与字符串之间的双向转换机制),要么使用Hadoop提供的Stringifier类。DefaultStringifier使用Hadoop的序列化框架来序列化对象。
但是这种机制会加大Hadoop守护进程的内存开销压力,当几百个作业在系统中同时运行时这种现象尤为突出。因此,这种机制并不适合传输多达几千字节的数据量。每次读取配置时,所有项都被读入到内存(即使暂时不用的属性项也不例外)。MR1中,作业配置由jobtracker、tasktracker和子JVM读取,jobtracker和tasktracker均不使用用户的属性,因此这种做法有时既浪费时间,又浪费内存。

代码如下

package com.zhen.mapreduce.sideData.job;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; /**
* @author FengZhen
* @date 2018年9月23日
* 边数据(通过job configuration来设置)
*/
public class SideData extends Configured implements Tool{ static class SideDataMapper extends Mapper<LongWritable, Text, Text, Text>{
String sideDataValue = "";
@Override
protected void setup(Mapper<LongWritable, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
sideDataValue = context.getConfiguration().get("sideData_test_key");
} @Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
context.write(value, new Text(sideDataValue));
}
} public int run(String[] args) throws Exception { Configuration configuration = new Configuration();
configuration.set("sideData_test_key", "sideData_test_value");
Job job = Job.getInstance(configuration);
job.setJobName("SideData");
job.setJarByClass(getClass()); job.setMapperClass(SideDataMapper.class); job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class); job.setNumReduceTasks(0); FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) {
try {
String[] params = new String[] {
"hdfs://fz/user/hdfs/MapReduce/data/sideData/job/input",
"hdfs://fz/user/hdfs/MapReduce/data/sideData/job/output"
};
int exitCode = ToolRunner.run(new SideData(), params);
System.exit(exitCode);
} catch (Exception e) {
e.printStackTrace();
}
} }

 

分布式缓存

与在作业配置中序列化边数据的技术相比,Hadoop的分布式缓存机制更受青睐,它能够在任务运行过程中及时地将文件和存档复制到任务节点以供使用。为了节约网络带宽,在每一个作业中,各个文件通常只需复制到一个节点一次。

1.用法

对于使用GenericOptionsParser的工具来说,用户可以使用-files选项指定待分发的文件,文件内包含以逗号隔开的URL列表。文件可以存放在本地文件系统、HDFS或其他Hadoop可读文件系统之中(如S3).如果尚未指定文件系统,则这些文件被默认是本地的。即使默认文件系统并非本地文件系统,这也是成立的。
用户可以使用-archives选项向自己的任务中复制存档文件(JAR文件、ZIP文件、tar文件和gzipped tar文件),这些文件会被解档到任务节点。-libjars选项会把JAR文件添加到mapper和reducer任务的类路径中。如果作业JAR文件并非包含很多库JAR文件,这点会很有用。

代码如下

package com.zhen.mapreduce.sideData.distributedCache;

import java.io.File;
import java.io.IOException; import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.log4j.Logger; /**
* @author FengZhen
* @date 2018年9月23日
* 读取本地文件边数据
* 读取不到该文件
* hadoop jar SideData.jar com.zhen.mapreduce.sideData.distributedCache.MaxTemperatureByStationNameUsingDistributedCacheFile -files /usr/local/test/mr/stations-fixed-width.txt
*/
public class MaxTemperatureByStationNameUsingDistributedCacheFile extends Configured implements Tool{ static Logger logger = Logger.getLogger(MaxTemperatureByStationNameUsingDistributedCacheFile.class); static enum StationFile{STATION_SIZE}; static class StationTemperatureMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
private NcdcRecordParser parser = new NcdcRecordParser();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
parser.parse(value.toString());
if (parser.isValidTemperature()) {
context.write(new Text(parser.getStationId()), new IntWritable(parser.getTemperature()));
}
}
} static class MaxTemperatureReducerWithStationLookup extends Reducer<Text, IntWritable, Text, IntWritable>{
private NcdcStationMetadata metadata; @Override
protected void setup(Reducer<Text, IntWritable, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
metadata = new NcdcStationMetadata();
File file = new File("stations-fixed-width.txt");
metadata.initialize(file);
context.getCounter(StationFile.STATION_SIZE).setValue(metadata.getStationMap().size());
} @Override
protected void reduce(Text key, Iterable<IntWritable> values,
Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
String stationName = metadata.getStationName(key.toString());
int maxValue = Integer.MIN_VALUE;
for (IntWritable value : values) {
maxValue = Math.max(maxValue, value.get());
}
context.write(new Text(stationName), new IntWritable(maxValue));
}
} public int run(String[] args) throws Exception {
Job job = Job.getInstance(getConf());
job.setJobName("MaxTemperatureByStationNameUsingDistributedCacheFile");
job.setJarByClass(getClass()); job.setMapperClass(StationTemperatureMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class); job.setReducerClass(MaxTemperatureReducerWithStationLookup.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class); FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) {
try {
String[] params = new String[] {
"hdfs://fz/user/hdfs/MapReduce/data/sideData/distributedCache/input",
"hdfs://fz/user/hdfs/MapReduce/data/sideData/distributedCache/output"
};
int exitCode = ToolRunner.run(new MaxTemperatureByStationNameUsingDistributedCacheFile(), params);
System.exit(exitCode);
} catch (Exception e) {
e.printStackTrace();
}
} }

 解析天气数据

package com.zhen.mapreduce.sideData.distributedCache;

import java.io.Serializable;

/**
* @author FengZhen
* @date 2018年9月9日
* 解析天气数据
*/
public class NcdcRecordParser implements Serializable{ private static final long serialVersionUID = 1L; /**
* 气象台ID
*/
private String stationId;
/**
* 时间
*/
private long timeStamp;
/**
* 气温
*/
private Integer temperature; /**
* 解析
* @param value
*/
public void parse(String value) {
String[] values = value.split(",");
if (values.length >= 3) {
stationId = values[0];
timeStamp = Long.parseLong(values[1]);
temperature = Integer.valueOf(values[2]);
}
} /**
* 校验是否合格
* @return
*/
public boolean isValidTemperature() {
return null != temperature;
} public String getStationId() {
return stationId;
} public void setStationId(String stationId) {
this.stationId = stationId;
} public long getTimeStamp() {
return timeStamp;
} public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp;
} public Integer getTemperature() {
return temperature;
} public void setTemperature(Integer temperature) {
this.temperature = temperature;
} }

 解析气象站数据

package com.zhen.mapreduce.sideData.distributedCache;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map; /**
* @author FengZhen
* @date 2018年9月23日
* 解析边数据
*/
public class NcdcStationMetadata { /**
* 存放气象站ID和name
*/
private Map<String, String> stationMap = new HashMap<String, String>(); public Map<String, String> getStationMap() {
return stationMap;
} public void setStationMap(Map<String, String> stationMap) {
this.stationMap = stationMap;
} /**
* 根据ID获取name
* @param stationId
* @return
*/
public String getStationName(String stationId) {
return stationMap.get(stationId);
} /**
* 解析
* @param value
*/
public boolean parse(String value) {
String[] values = value.split(",");
if (values.length >= 2) {
String stationId = values[0];
String stationName = values[1];
if (null == stationMap) {
stationMap = new HashMap<String, String>();
}
stationMap.put(stationId, stationName);
return true;
}
return false;
} /**
* 解析气象站数据文件
* @param file
*/
public void initialize(File file) { BufferedReader reader=null;
String temp=null;
try{
reader=new BufferedReader(new FileReader(file));
System.out.println("------------------start------------------");
while((temp=reader.readLine())!=null){
System.out.println(temp);
parse(temp);
}
System.out.println("------------------end------------------");
}
catch(Exception e){
e.printStackTrace();
}
finally{
if(reader!=null){
try{
reader.close();
}
catch(Exception e){
e.printStackTrace();
}
}
} } }

  

2.工作机制

当用户启动一个作业,Hadoop会把由-files、-archives和-libjars等选项所指定的文件复制到分布式文件系统(一般是HDFS)之中。接着,在任务运行之前,tasktracker(YARN中NodeManager)将文件从分布式文件系统复制到本地磁盘(缓存)使任务能够访问文件。此时,这些文件就被视为本地化了。从任务的角度看,这些文件就已经在那儿了(它并不关心这些文件是否来自HDFS)。此外,由-libjars指定的文件会在任务启动前添加到任务的类路径(classpath)中。

tasktracker(YARN中NodeManager)为缓存中的文件各维护一个计数器来统计这些文件的被使用情况。当任务即将运行时,该任务所使用的所有文件的对应计数器值增1;当任务执行完毕之后,这些计数器值均减1.当相关计数器值为0时,表明该文件没有被任何任务使用,可以从缓存中移除。缓存的容量是有限的(默认10GB),因此需要经常删除无用的文件以腾出空间来装载新文件。缓存大小可以通过属性local.cache.size进行配置,以字节为单位。

尽管该机制并不确保在同一个tasktracker上运行的同一作业的后续任务肯定能在缓存中找到文件,但是成功的概率相当大。原因在于作业的多个任务在调度之后几乎同时开始运行,因此,不会有足够多的其他作业在运行而导致原始任务的文件从缓存中被删除。
文件存放在tasktracker的${mapred.local.dir}/taskTracker/archive目录下。但是应用程序不必知道这一点,因为这些文件同时以符号链接的方式指向任务的工作目录。

3.分布式缓存API

由于可以通过GenericOptionsParser间接使用分布式缓存,大多数应用不需要使用分布式缓存API。然而,一些应用程序需要用到分布式缓存的更高级的特性,这就需要直接使用API了。API包括两部分:将数据放到缓存中的方法,以及从缓存中读取数据的方法。

public void addCacheFile(URI uri)
public void addCacheArchive(URI uri)
public void setCacheFiles(URI[] files)
public void setCacheArchives(URI[] archives)
public void addFileToClassPath(Path file)
public void addArchiveToClassPath(Path archive)

在缓存中可以存放两类对象:文件(files)和存档(archives)。文件被直接放置在任务节点上,而存档则会被解档之后再将具体文件放置在任务节点上。每种对象类型都包含三种方法:addCacheXXX()、setCacheXXXs()和addXXXToClassPath()。其中,addCacheXXX()方法将文件或存档添加到分布式缓存,setCacheXXXs()方法将一次性向分布式缓存中添加一组文件或存档(之前调用所生成的集合将被替换),addXXXToClassPath()方法将文件或存档添加到MapReduce任务的类路径。
代码如下(有问题,读不到文件)

package com.zhen.mapreduce.sideData.distributedCache;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI; import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.log4j.Logger; /**
* @author FengZhen
* @date 2018年9月23日
* 读取本地文件边数据
* 有问题,空指针,读不到文件
* hadoop jar SideData.jar com.zhen.mapreduce.sideData.distributedCache.MaxTemperatureByStationNameUsingDistributedCacheFileAPI
*/
public class MaxTemperatureByStationNameUsingDistributedCacheFileAPI extends Configured implements Tool{ static Logger logger = Logger.getLogger(MaxTemperatureByStationNameUsingDistributedCacheFileAPI.class); static enum StationFile{STATION_SIZE}; static class StationTemperatureMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
private NcdcRecordParser parser = new NcdcRecordParser();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
parser.parse(value.toString());
if (parser.isValidTemperature()) {
context.write(new Text(parser.getStationId()), new IntWritable(parser.getTemperature()));
}
}
} static class MaxTemperatureReducerWithStationLookup extends Reducer<Text, IntWritable, Text, IntWritable>{
private NcdcStationMetadata metadata; @Override
protected void setup(Reducer<Text, IntWritable, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
metadata = new NcdcStationMetadata();
URI[] localPaths = context.getCacheFiles();
if (localPaths.length == 0) {
throw new FileNotFoundException("Distributed cache file not found.");
}
File file = new File(localPaths[0].getPath().toString());
metadata.initialize(file);
context.getCounter(StationFile.STATION_SIZE).setValue(metadata.getStationMap().size()); } @Override
protected void reduce(Text key, Iterable<IntWritable> values,
Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
String stationName = metadata.getStationName(key.toString());
int maxValue = Integer.MIN_VALUE;
for (IntWritable value : values) {
maxValue = Math.max(maxValue, value.get());
}
context.write(new Text(stationName), new IntWritable(maxValue));
}
} public int run(String[] args) throws Exception {
Job job = Job.getInstance(getConf());
job.setJobName("MaxTemperatureByStationNameUsingDistributedCacheFileAPI");
job.setJarByClass(getClass()); job.setMapperClass(StationTemperatureMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class); job.setReducerClass(MaxTemperatureReducerWithStationLookup.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class); job.addCacheFile(new URI("hdfs://fz/user/hdfs/MapReduce/data/sideData/distributedCache/stations-fixed-width.txt"));
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) {
try {
String[] params = new String[] {
"hdfs://fz/user/hdfs/MapReduce/data/sideData/distributedCache/input",
"hdfs://fz/user/hdfs/MapReduce/data/sideData/distributedCache/output"
};
int exitCode = ToolRunner.run(new MaxTemperatureByStationNameUsingDistributedCacheFileAPI(), params);
System.exit(exitCode);
} catch (Exception e) {
e.printStackTrace();
}
} }

  

MapReduce-边数据的更多相关文章

  1. MapReduce的数据流程、执行流程

    MapReduce的数据流程: 预先加载本地的输入文件 经过MAP处理产生中间结果 经过shuffle程序将相同key的中间结果分发到同一节点上处理 Recude处理产生结果输出 将结果输出保存在hd ...

  2. Hadoop基础-MapReduce的数据倾斜解决方案

    Hadoop基础-MapReduce的数据倾斜解决方案 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.数据倾斜简介 1>.什么是数据倾斜 答:大量数据涌入到某一节点,导致 ...

  3. mapreduce清洗数据

    继上篇 MapReduce清洗数据 package mapreduce; import java.io.IOException; import org.apache.hadoop.conf.Confi ...

  4. Hadoop第7周练习—MapReduce进行数据查询和实现推简单荐系统

    1.1 1.2 :计算员工相关 2.1 内容 :求各个部门的总工资 :求各个部门的人数和平均工资 :求每个部门最早进入公司的员工姓名 :求各个城市的员工的总工资 :列出工资比上司高的员工姓名及其工资 ...

  5. MapReduce 实现数据join操作

    前段时间有一个业务需求,要在外网商品(TOPB2C)信息中加入 联营自营 识别的字段.但存在的一个问题是,商品信息 和 自营联营标示数据是 两份数据:商品信息较大,是存放在hbase中.他们之前唯一的 ...

  6. MongoDB 的 MapReduce 大数据统计统计挖掘

    MongoDB虽然不像我们常用的mysql,sqlserver,oracle等关系型数据库有group by函数那样方便分组,但是MongoDB要实现分组也有3个办法: * Mongodb三种分组方式 ...

  7. Mapreduce——视频播放数据分类统计

    很多视频网站都有电视剧热度排名,一般是依据用户在自己站的行为数据所体现出的受欢迎程度来排名.这里有一份来自优酷.爱奇艺.搜索视频等五大视频网站的一份视频播放数据,我们利用这份数据做些有意义的事情. 金 ...

  8. MapReduce实例(数据去重)

    数据去重: 原理(理解):Mapreduce程序首先应该确认<k3,v3>,根据<k3,v3>确定<k2,v2>,原始数据中出现次数超过一次的数据在输出文件中只出现 ...

  9. 利用MapReduce实现数据去重

    数据去重主要是为了利用并行化的思想对数据进行有意义的筛选. 统计大数据集上的数据种类个数.从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据去重. 示例文件内容: 此处应有示例文件 设计思路 数据 ...

  10. hadoop mapreduce实现数据去重

    实现原理分析: map函数数将输入的文本按照行读取,   并将Key--每一行的内容   输出    value--空. reduce  会自动统计所有的key,我们让reduce输出key-> ...

随机推荐

  1. UIAlertController custom font, size, color

    本文转载至 http://stackoverflow.com/questions/26460706/uialertcontroller-custom-font-size-color up vote2d ...

  2. Ninject学习笔记<四>

    前言 前段时间看Mvc最佳实践时,认识了一个轻量级的IOC框架:Ninject.通过google搜索发现它是一个开源项目,最新源代码地址是:http://github.com/enkari/ninje ...

  3. form表单提交中文乱码(前台中文到JAVA后台乱码)问题及解决

    form表单提交中文乱码(前台中文到JAVA后台乱码)问题及解决 一.问题: 页面输入框中的中文内容,在后台乱码,导致搜索功能失效:(详细可以见后面的重现) 二.原因: 浏览器对于数据的默认编码格式为 ...

  4. iOS 多线程之 NSOperation 的基本使用

    1.NSOperation,NSOperationQueue 简介 NSOperation,NSOperationQueue是苹果提供给我们的一套多线程解决方案.实际上 NSOperation.NSO ...

  5. Oracle ErrorStack 使用和阅读具体解释

    一.概述 在Oracle数据库执行过程中,我们常常会遇到这样或那样的错误.可是错误的提示并不详细,加大了我们在诊断问题时的难度. ErrorStack是Oracle提供的一种对于错误堆栈进行跟踪的方法 ...

  6. 字符串之_strncat

    功能:将src的前n个字符添加到dest的后面 输入:dest,src,count 返回:ret #include <iostream> #include <assert.h> ...

  7. python学习之路-第二天-常见的注意事项(代码风格、运算符、优先级、控制语句)

    总结了今天学习几个注意事项: 对代码声明变量的时候没必要像以前写java或者c代码要声明数据类型,只需要赋值即可 代码一行基本只写一句逻辑行,而且尽量不在python里面写':' 明确的行连接'',暗 ...

  8. PyQt4 进度条和日历 代码

    # -*- coding: utf-8 -*- """ ------------------------------------------------- File Na ...

  9. C#托管代码 CLR

    托管代码 是直接编译成机器码,而是编译成中间语言 IL,由 CLR 托管运行. 托管代码就是把底层的一些操作(如内存的读取,释放)全都封装起来了,把有关内存管理的操作全都由CLR来管理, C#使用垃圾 ...

  10. 分层架构下的纯JDBC事务控制简单解决方案【转】

    http://blog.csdn.net/qjyong/article/details/5464835 对目前的JavaEE企业应用开发来说,基本都会采用分层的架构, 这样可以分散关注.松散耦合.逻辑 ...