热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MapReduceDesignPatterns(chapter2(part3))(四)

InvertedIndexSummarizationsPatternDescription反向索引模式在MapReduce分析中经常作为一个例子。我们将会讨论我们要创建的t

Inverted Index Summarizations

Pattern Description

反向索引模式在MapReduce分析中经常作为一个例子。我们将会讨论我们要创建的term跟标识符之间映射的一般情况。

 

Intent

根据数据集生成索引,用于快速搜索或数据的富集能力。

Motivation

根据关键词索引大数据非常方便,搜索能追踪term找到包含指定值的记录。创建索引需要之前进行额外的处理,花时间去做这项工作能有效减少我们寻找东西的时间。

 

搜索引擎为了提高搜索性能创建索引。设想键入一个关键词,让引擎去互联网抓取信息并创建一个页面的列表返回给你。这样的查询会耗费巨大的时间。如果创建一个反向索引,搜索引擎会在之前就知道跟关键词相关的互联网页面,结果很简单的展现给用户。这种索引也经常注入数据库中,为了更快的查询响应。使用MapReduce创建反向索引是相对简单的任务,因为框架处理绝大多数工作。

Applicability

反向索引用在搜索查询时需要快速响应的情况下。查询的结果能被预先处理并放入数据库。

Structure

2-5展示了MapReduce中执行反向索引的组织结构。下面详细介绍MapReduce组件各部分的功能:

 

·mapper输出需要索引的字段作为key,唯一标识符作为value

·如果使用identity reducer,可以不用combiner,因为这种情况下combiner仅仅执行一些不需要的处理。很多实现在输出到文件系统之前将组和值关联起来。这种情况下,combiner就能使用。这里对字节计数没有有益的影响,不像其它模式里的combiner,但也会有一种改进。

·partitioner负责决定含有相同key的值能够拷贝到同一个reducer中。如果中间键不是平均分发的,为了更有效的负载均衡,可以重新定义partitioner

·reducer将接收一系列唯一的记录标识跟输入key关联。标识符可以是一些唯一的分隔符连接起来,使输出为每组一个键值对,也可以是输入key里的value,正如identity reducer

Figure 2-5. The structure of the inverted index pattern

 

Consequences

最终的输出是包含字段到一系列包含相关字段值的记录的标识符的映射的分片文件的集合。

Performance analysis

创建反向索引的性能主要取决于mapper中解析内容的计算代价,索引键的基数,和每个key中内容标识符的数量。

 

Mapper中解析文本或其他类型的内容,有时是计算紧张的操作。这个问题对半结构化的数据特别突出,例如xmljson,因为这种类型可能需要解析任意的信息量,到一个可用的对象中。尽可能高效的解析传入的记录来提高整个job的性能,是非常重要的。

 

如果唯一键的数量和标识符的数量非常巨大,将会发送到reducer更多的数据。越多的数据发送到reducer,你应该增加reducer的数量来提高reduce阶段的并行度。

 

反向索引对索引键中的热点非常敏感,因为索引键很少是均匀分布的。例如,reducer在文本搜索中处理单词”the”,将会变得非常繁忙,因为文本中有大量的单词“the”。这将因为个别执行时间较长的reducers而影响到整个job的执行。为了避免这个问题,你可能需要实现一个自定义的partitioner,或忽略这个关键词,不给值。

Inverted Index Example

Wikipedia reference inverted index

创建反向索引对MapReduce来说是一项简单的工作,它经常作为初学者继word count之后的第二个例子。与word count很像,大多数的工作由MapReduce框架来做。

 

假设要在每个引用到stackOverflow评论的wikipedia页面添加stackoverflow的链接。下面的例子分析每一个stackOverflow的评论来找出是否从wikipedia链接过来的。如果是,链接跟评论的id一同输出,生成反向索引。到reduce阶段时,引用到相同链接的评论id会分组到一起。然后通过空格分隔符把分组连接起来,输出到文件系统。至此,可以用这些数据文件中引用到wikipedia的所有评论更新wikipedia页面。

 

问题:给出用户评论数据,在一系列回答问题的id上创建wikipedia的反向索引

 

Mapper codeMapper解析stackOverflow帖子数据输出wikipedia url和回帖记录的id。从xml属性中抽取内容,提交类型(发帖or回帖),记录id。如果提交类型是提问,不是回答,标识为“2”(代码中明明写的是1,我的钛合金狗眼。。。)(本人认为有误),然后解析内容,找到wikipedia url。使用getWikipediaURL方法,传入非转义的html文本,找到就返回,否则返回空。这段代码就不在这里列出了。如果url找到,就把它当做key,记录id作为值一并输出。

 

public static classWikipediaExtractor extends

Mapper<Object,Text, Text, Text> {

privateText link =new Text();

privateText outkey =new Text();

publicvoid map(Object key,Text value,Context context)

throwsIOException,InterruptedException {

Map<String,String> parsed = MRDPUtils.transformXmlToMap(value

.toString());

// Grab the necessary XML attributes

String txt= parsed.get("Body");

String posttype= parsed.get("PostTypeId");

String row_id= parsed.get("Id");

// if the body is null, or the post is a question (1), skip

if(txt== null|| (posttype!= null&& posttype.equals("1"))) {

return;

}

// Unescape the HTML because the SO data is escaped.

txt= StringEscapeUtils.unescapeHtml(txt.toLowerCase());

link.set(getWikipediaURL(txt));

outkey.set(row_id);

context.write(link,outkey);

}

}

 

Reduce codeReducer会迭代所有值,以string类型,空格为分隔符,把记录id连接起来。输入key作为输出keyvalue是连接起来的string

 

public static classConcatenator extendsReducer<Text,Text,Text,Text> {

privateText result =new Text();

publicvoid reduce(Text key,Iterable<Text>values, Context context)

throwsIOException,InterruptedException {

StringBuilder sb= newStringBuilder();

booleanfirst =true;

for(Text id: values) {

if(first) {

first= false;

}else {

sb.append(" ");

}

sb.append(id.toString());

}

result.set(sb.toString());

context.write(key,result);

}

}

 

Combiner optimizationCombiner可以在reduce阶段之前做一些预连接。因为所有的记录id简单连接在一起,需要拷贝到reducer的字节量比数值聚合模式中的要多。Reduce代码可以用作combiner

 

Counting with Counters

Pattern Description

这种模式利用了MapReduce框架本身的计数器功能在map端做全局的计算,不做任何输出。

Intent

一种获取大数据量下总体计数值得有效手段。

Motivation

一个计数或总和能告诉你数据某个字段的信息,或整个数据信息。根据每个小时的计数值就能得到有用的直方图。这种模式也可以用类似word count的程序计算:这种情况下,对每个输入记录,输出相同记录作为key,表示这一小时处理了这条记录,并给计数1。唯一的reduce会对所有输入值求和,输出这一小时内最终的记录条数。这种使用起来很简单,但用计数器会更高效。不会写任何的键值对,只利用框架的计数机制跟踪输入的记录条数。这样就不需要reduce阶段并不需要求和。框架会监控计数器的名字和它们相关的值,并根据所有tasks聚合这些值,包括任何失败的task attempts

 

例如你想找到每天你的员工大量访问站点的次数。假设你有若干员工,可以对web日志解析时用条件过滤。不用输出员工的姓名和计数1,你可以创建一个计数器,包含员工id,自增1。在job的最后,简单的从框架获取计数器并保存到任何你想要的的地方。

 

许多计数器是内建在框架里的,例如,输入输出记录数和字节数。Hadoop允许程序猿自定义任何可能需要的计数器。这种模式描述了怎样利用这种自定义计数器从数据集收集计数或合计指标。使用计数器的主要好处就是所有的计数都在map阶段完成。

 

Notice:使用计数器需要清楚的是它们都存储在jobTracker的内存里。Map任务序列化它们,连同更新状态被发送。为了运行正常且jobTracker不会出问题,计数器的数量应该在10-100个,计数器不仅仅只用来聚合MapReduce job的统计值。新版本的hadoop限制了计数器的数量,以防给jobTracker带来损害。你最不想看到的事情就是由于定义上百个计数器而使jobTracker宕机。

 

Applicability

用计数器计数适用的情况:

·在大数据集上收集计数或求和。

·创建的计数器个数较小,两位数以内。

Structure

2-6展示了这种模式的组织结构。

·mapper每次处理每条输入记录根据某一条件自增计数器。计数器可以是自增1的计数,也可以是自增某数值的求和计算。这些计数器在TaskTracker聚合以后加到jobTracker上,直到job结束。失败任务的计数器在jobTracker最终求和时不会计算在内。

·因为job只有map,所以没有combinerpartitionerreducer

Consequences

最终的输出是从job框架获取的计数器的集合。分析本身没有实际的输出。但是job需要一个输出目录。这个目录将会产生几个空文件风别对应几个map任务。Job完成时目录应该被删掉。

 

Known uses

统计记录的条数:

简单的统计给定时间段内记录条数是很常见的,这是框架提供的典型的计数器。

统计数量较小的唯一事件:

计数器也可以在程序运行中创建,用字符串变量。你可能现在就知道值是什么,但计数器不必提前创建。简单的使用字段值创建一个计数器并自增,足够解决这类问题。只需要保证计数器数量要小。

求和:

计数器能用来做数据字段的求和。但不是在reduce端执行求和,仅仅创建并使用它求字段值的和。

 

Figure 2-6. The structure of the counting with counters pattern

Performance analysis

使用计数器能很快的完成计算,因为数据仅仅在map中处理,没有输出要写。性能主要取决于执行的map的个数和处理每条记录花费的时间。

Counting with Counters Example

Number of users per state

对于这个例子,我们只用map来统计每个州下用户的数量。位置属性是用户键入的值,不需要任何具体的输入。由于此,会存在大量的空字段,还有编造的位置数据。我们需要处理这个问题,处理每条输入记录时也要保证不要创建太多的计数器。我们创建计数器之前要检验位置数据是否包含状态缩写码。这样创建最多52个计数器—50个州(美帝),2nullempty。这对于jobTracker来说是很容易管理的计数器的数量,你的程序不能比这个多很多。

 

问题:用hadoop自定义计数器统计每个州的用户数。

 

Mapper codeMapper读取每条记录并得到这个用户的位置。位置是用空壳分隔的,用代表州的信息搜索。我们把所有州的缩写放进内存,来防止过多的计数器被创建。位置数据仅仅是用户设置的字符串,并不是其他数据结构。如果州被识别出来,计数器递增1。计数器通过组和名字标识。这里,组是州,一个公共的string类型变量,名字是州的缩写代码。

 

public static classCountNumUsersByStateMapper extends

Mapper<Object,Text, NullWritable, NullWritable> {

public static finalString STATE_COUNTER_GROUP ="State";

public static finalString UNKNOWN_COUNTER ="Unknown";

public static finalString NULL_OR_EMPTY_COUNTER ="Null or Empty";

privateString[]statesArray =new String[] {"AL", "AK", "AZ","AR",

"CA","CO", "CT", "DE","FL", "GA", "HI","ID", "IL", "IN",

"IA","KS", "KY", "LA","ME", "MD", "MA","MI", "MN", "MS",

"MO","MT", "NE", "NV","NH", "NJ", "NM","NY", "NC", "ND",

"OH","OK", "OR", "PA","RI", "SC", "SF","TN", "TX", "UT",

"VT","VA", "WA", "WV","WI", "WY" };

privateHashSet<String>states = new HashSet<String>(

Arrays.asList(statesArray));

publicvoid map(Object key,Text value,Context context)

throwsIOException,InterruptedException {

Map<String,String> parsed = MRDPUtils.transformXmlToMap(value

.toString());

// Get the value for the Location attribute

String location= parsed.get("Location");

// Look for a state abbreviation code if the

// location is not null or empty

if(location!= null&& !location.isEmpty()) {

// Make location uppercase and split on white space

String[]tokens = location.toUpperCase().split("\\s");

// For each token

booleanunknown =true;

for(String state: tokens) {

// Check if it is a state

if(states.contains(state)) {

// If so, increment the state's counter by 1

// and flag it as not unknown

context.getCounter(STATE_COUNTER_GROUP,state)

.increment(1);

unknown= false;

break;

}

}

// If the state is unknown, increment the UNKNOWN_COUNTER counter

if(unknown) {

context.getCounter(STATE_COUNTER_GROUP,UNKNOWN_COUNTER)

.increment(1);

}

}else {

// If it is empty or null, increment the

// NULL_OR_EMPTY_COUNTER counter by 1

context.getCounter(STATE_COUNTER_GROUP,

NULL_OR_EMPTY_COUNTER).increment(1);

}

}

}

 

Driver code。驱动代码大部分都是样板,不用动,这个例子要改一些:job完成后获取计数器。Job成功以后就把结果打印到标准输出。这些计数器的值也会在job完成后输出的指定目录,所以把他们写到标准输出可能是多余的,如果你会通过查看日志文件获取他们。输出目录随后被删掉,不管成功与否,因为这样的job创建的不是有形产出,基本没意义。

 

...

intcode =job.waitForCompletion(true) ?0 : 1;

if(code== 0) {

for(Counter counter: job.getCounters().getGroup(

CountNumUsersByStateMapper.STATE_COUNTER_GROUP)) {

System.out.println(counter.getDisplayName() + "\t"

+counter.getValue());

}

}

// Clean up empty output directory

FileSystem.get(conf).delete(outputDir,true);

System.exit(code);

 


推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
author-avatar
mobiledu2502896655
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有