这里有一个日志文件:
2011-10-26 06:11:35 user1 210.77.23.12
2011-10-26 06:11:45 user2 210.77.23.17
2011-10-26 06:11:46 user3 210.77.23.12
2011-10-26 06:11:47 user2 210.77.23.89
2011-10-26 06:11:48 user2 210.77.23.12
2011-10-26 06:11:52 user3 210.77.23.12
2011-10-26 06:11:53 user2 210.77.23.12
...
我想使用MapReduce按照第三个字段(用户)的日志记录次数对每行进行降序排序。换句话说,我希望结果显示为:
user2 4
user3 2
user1 1
现在我有两个问题:
默认情况下,MapReduce会用空格和回车拆分日志文件,但我每行只需要第三个字段,也就是说,我不在乎
2011-10-26
、06:11:35
、210.77.23.12
等字段,如何让MapReduce省略它们,并提取>user字段?默认情况下,MapReduce将按键而不是值对结果进行排序。如何让MapReduce按值(日志记录时间)对结果进行排序?
谢谢。
对于您的第一个问题:
您可能应该将整行传递给映射器,每次只保留用于映射和映射的第三个令牌(user
,1)。
public class AnalyzeLogs
{
public static class FindFriendMapper extends Mapper<Object, Text, Text, IntWritable> {
public void map(Object, Text value, Context context) throws IOException, InterruptedException
{
String tempStrings[] = value.toString().split(",");
context.write(new Text(tempStrings[2]), new IntWritable(1));
}
}
对于你的第二个问题,我相信你无法避免在那之后有第二份MR工作(我想不出其他办法)。因此,第一个作业的reducer只会聚合值,并为每个键提供一个总和,按键排序。这还不是你所需要的。
因此,您将此作业的输出作为输入传递给第二个MR作业。这项工作的目的是在传递给减速器之前,按值进行某种特殊的排序(这绝对不会起到任何作用)。
我们的第二项工作的映射器将如下:
public static class SortLogsMapper extends Mapper<Object, Text, Text, NullWritable> {
public void map(Object, Text value, Context context) throws IOException, InterruptedException
{
context.write(value, new NullWritable());
}
正如您所看到的,我们根本没有使用此映射器的值。相反,我们创建了一个键,包含我们的值(我们的键采用key1 value1
格式)。现在要做的是向框架指定它应该基于value1
而不是整个key1 value1
进行排序。因此,我们将实现一个自定义的SortComparator
:
public static class LogDescComparator extends WritableComparator
{
protected LogDescComparator()
{
super(Text.class, true);
}
@Override
public int compare(WritableComparable w1, WritableComparable w2)
{
Text t1 = (Text) w1;
Text t2 = (Text) w2;
String[] t1Items = t1.toString().split(" "); //probably it's a " "
String[] t2Items = t2.toString().split(" ");
String t1Value = t1Items[1];
String t2Value = t2Items[1];
int comp = t2Value.compareTo(t1Value); // We compare using "real" value part of our synthetic key in Descending order
return comp;
}
}
您可以将自定义比较器设置为:job.setSortComparatorClass(LogDescComparator.class);
这项工作的减速器应该什么都不做。但是,如果我们不设置reducer,映射器键的排序将不会完成(我们需要这样做)。因此,您需要将IdentityReducer
设置为第二个MR作业的Reducer,以不进行缩减,但仍然确保映射器的合成密钥按照我们指定的方式排序。