hadoop计算需要在hdfs文件系统上进行,因此每次计算之前必须把需要用到的文件(我们称为原始文件)都上传到hdfs上。文件上传到hdfs上通常有两种方法:
a hadoop自带的dfs服务,put;
b hadoop的API,Writer对象可以实现这一功能;
将a、b方案进行对比,如下:
1 空间:方案a在hdfs上占用空间同本地,因此假设只上传日志文件,则保存一个月日志文件将消耗掉约10T空间,如果加上这期间的各种维表、事实表,将占用大约25T空间
方案b经测试,压缩比大约为3~4:1,因此假设hdfs空间为100T,原来只能保存约4个月的数据,现在可以保存约1年
2 上传时间:方案a的上传时间经测试,200G数据上传约1小时
方案b的上传时间,程序不做任何优化,大约是以上的4~6倍,但存在一定程度提升速度的余地
3 运算时间:经过对200G数据,大约4亿条记录的测试,如果程序以IO操作为主,则压缩数据的计算可以提高大约50%的速度,但如果程序以内存操作为主,则只能提高5%~10%的速度
4 其它:未压缩的数据还有一个好处是可以直接在hdfs上查看原始数据。压缩数据想看原始数据只能用程序把它导到本地,或者利用本地备份数据
压缩格式:按照hadoop api的介绍,压缩格式分两种:BLOCK和RECORD,其中RECORD是只对value进行压缩,一般采用BLOCK进行压缩。
对压缩文件进行计算,需要用SequenceFileInputFormat类来读入压缩文件,以下是计算程序的典型配置代码:
压缩wordcount程序
package fh;
/*
* 题目描述:在英文环境下,给定一篇文章,统计每个单词出现的次数,单词不区分大小写,标点符号不进行统计
* 程序设计约束:程序需要两个输入参数,第一个为输入文件的路径,第二个为输出文件的路径,输出文件的格式为
*/
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile.CompressionType;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.mapred.JobConf;
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.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class MyWordCount {
enum Counter {
LINESKIP, // 出错行
}
public static class TokenizerMapper extends
Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
// private String delim=" ,.?!:;-_()[]{}'"+'"';
private String pattern = "[^\\w]"; // 正则表达式,代表不是0-9, a-z, A-Z的所有其它字符
public void map(Object key, Text value, Context context) {
try {
String line = value.toString().toLowerCase(); // 全部转为小写字母
line = line.replaceAll(pattern, " "); // 将非0-9, a-z, A-Z的字符替换为空格
StringTokenizer itr = new StringTokenizer(line);
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
} catch (Exception e) {
// 出错让计数器加一
context.getCounter(Counter.LINESKIP).increment(1);
return;
}
}
}
public static class IntSumReducer extends
Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
int sum = 0;
/*
* 获取conf配置,获取其中内容 Configuration conf=context.getConfiguration();
* System.out.println(conf.get("fh"));
*/
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
key.set(key.toString() + ":");
// System.out.println(key.toString() + "--" + result);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
//设置对map输出文件进行压缩
conf.setBoolean("mapred.compress.map.output", true);
conf.setClass("mapred.map.output.compression.codec", GzipCodec.class,CompressionCodec.class);
String[] otherArgs = new GenericOptionsParser(conf, args)
.getRemainingArgs();
if (otherArgs.length != 2) {
System.err.println("Usage: wordcount <in> <out>");
System.exit(2);
}
Job job = new Job();
job.setJobName("wordcount");
// 如果需要打成jar运行,需要下面这句
job.setJarByClass(MyWordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//压缩输出文件---压缩文件到hdfs上,不能直接查看,需要下载到本地查看
// FileOutputFormat.setCompressOutput(job, true);
// FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
// SequenceFileOutputFormat.setOutputCompressionType(job, CompressionType.BLOCK);//默认为RECORD,每条记录;建议改为BLOCK
SequenceFileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
未压缩的数据还有一个好处是可以直接在hdfs上查看原始数据。压缩数据想看原始数据只能用程序把它导到本地,或者利用本地备份数据