1.3 Java和Hive:词频统计算法
如果用户不是Java工程师,那么可以直接跳到下一节。
如果用户是名Java工程师,那么可能需要阅读本节,因为用户需要为其所在组织的Hive用户提供技术支持。你可能会质疑如何使用Hive解决自己的工作。如果是这样的话,那么可以先看看下面这个实现了之前我们所讨论的Word Count算法的例子,我们先学会使用Java MapReduce API,然后再学习如何使用Hive。
通常都会使用Word Count作为用户学习使用Java编写MapReduce程序的例子,因为这样用户可以关注于API。因此,Word Count已经成为Hadoop世界中的“Hello World”程序了。
Apache Hadoop 分支版本中包含有下面的这个Java实现[8]。如果读者并不了解Java(但是你仍在读本节内容的话),也不必担心,我们提供这个代码只是为了方便用户进行大小对比。
package org.myorg;
import java.io.IOException;
import java.util.*;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
public class WordCount {
public static class Map extends Mapper<LongWritable, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
StringTokenizer tokenizer = new StringTokenizer(line);
while (tokenizer.hasMoreTokens()) {
word.set(tokenizer.nextToken());
context.write(word, one);
}
}
}
public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
context.write(key, new IntWritable(sum));
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, "wordcount");
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setMapperClass(Map.class);
job.setReducerClass(Reduce.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}
上面是一个有63行的Java代码。我们不会详细解释其中的API[9]。如下是使用HiveQL进行的相同的运算,这时只有8行代码,而且不需要进行编译然后生成一个“JAR”(Java压缩包)文件。
CREATE TABLE docs (line STRING);
LOAD DATA INPATH 'docs' OVERWRITE INTO TABLE docs;
CREATE TABLE word*counts AS
SELECT word, count(1) AS count FROM
(SELECT explode(split(line, '\s')) AS word FROM docs) w
GROUP BY word
ORDER BY word;
我们稍后会解释所有这些HiveQL语法。
在上面两个例子中,都是使用尽可能简单的方法将文件中的内容分割成单词,也就是按照空格进行划分的。这个方法不能很好地处理标点,同时也不能识别同一个单词的单数和复数形式,等等。不过,这里这么使用已经可以达到我们的目的了。[10]
借助Java API可以定制和调整一个算法实现的每个细节。不过,大多数情况下,用户都不需要这个级别的控制,而且当用户需要控制所有那些细节时也会相当地放慢用户的开发进度。
如果你不是一名程序员,那么也就用不着写Java MapReduce代码了。不过,如果你已经熟悉SQL了,那么学习Hive将会相当地容易,而且很多程序也都很容易快速实现。