chapter4MapReduceandDesignPatterns.docx
- 文档编号:3711641
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:29
- 大小:320.72KB
chapter4MapReduceandDesignPatterns.docx
《chapter4MapReduceandDesignPatterns.docx》由会员分享,可在线阅读,更多相关《chapter4MapReduceandDesignPatterns.docx(29页珍藏版)》请在冰豆网上搜索。
chapter4MapReduceandDesignPatterns
Chapter4.DataOrganizationPatterns
与前面章节的过滤器相比,本章是关于数据重组。
个别记录的价值通常靠分区,分片,排序成倍增加。
特别是在分布式系统中,因为这能提高性能。
在很多组织结构方面,Hadoop和其它MapReduce使用案例仅仅是大数据分析平台上一片数据的处理。
数据通常被转换成跟其它系统有良好接口的形式,同样,数据也可能从原来状态转成一种新的状态,从而使MapReduce分析更容易。
本章包括下面几个子模式:
·分层结构模式
·分区和装箱模式
·全局排序和混洗模式
·生成数据模式
本章的模式通常一起使用来解决数据的组织问题。
例如,你可能想调整数据分层,装箱,然后对箱进行排序。
第六章的“JobChaining“详细介绍了怎用把各种模式组合起来解决复杂的问题。
StructuredtoHierarchical
PatternDescription
结构分层模式会根据数据创建新的不同结构的记录。
由于这种模式的重要性,在本章很多地方独立存在。
Intent
把基于行的数据转换成有层次的格式,例如,jsonxml。
Motivation
当从RDBMS向Hadoop系统迁移数据时,首先考虑的一件事就是重组数据成为一种有意义的结构。
因为hadoop不会关心数据格式,你应该充分利用分层次数据的优势避免做join。
例如:
我们的stackOverflow数据包含一张评论表,一张发帖表。
很明显这是存储在标准的sql数据库的。
当你访问发帖表时,所有数据库中的数据块要整合成一个视图来展现。
这使得想分析个别发帖时变得更复杂。
设想用发帖的长度跟评论的长度相关联,这需要首先做一次代价较高的join操作,然后抽取有用的数据。
如果换成根据发帖分组数据,使发帖跟相关联的评论,编辑,修改数据紧挨着(例如反规范化表数据),这样分析起来会更容易和直观。
这种情况下保存结构化数据完全达不到目的。
不幸的是,数据不总是分组在一块。
当某人回复了stackOverflow的某个问题,hadoop不能够把这条记录立刻插到层次数据中。
因此,用MapReduce创建非结构化记录只适用于周期性的批处理形式的业务逻辑。
另一种能平稳更新数据的方式是用Hbase。
Hbase能存储半结构化和层次样式的数据。
MongoDB也能很好的处理这种数据的排序。
Applicability
这种模式适合的场景:
·你的数据靠很多外键相联系
·你的数据是结构化的基于行的
Structure
图4-1展示了这种模式的结构,每部分组件描述如下:
·如果你想合并多个数据源成为一个有层次的数据结构,hadoop中有个类:
org.apache.hadoop.mapreduce.lib.input.MultipleInputs很适合使用(貌似很老的包)。
Mutipleinputs允许你对不同的input使用不同的inputpath和不同的mapper。
Driver里完成配置。
如果只有一个来源,则不需要这一步。
·mapper加载数据并解析成紧凑的格式从而使reducer更容易。
输出key应该是你想要标识的每一条层次记录的根。
例如,在stackOverflow例子里,根是发帖id。
也需要给没片数据标注来源信息。
也需要标识输出记录是发帖还是评论。
这样的话,就能简单连接这些标签并输出值。
·通常,这里用combiner起不了多大作用。
可以用相同的key对条目分组,一起发送,但这样没有压缩所起到的好处,因为要做的知识连接字符串,所以输出大小不变。
·reducer按key从不同的源接收数据。
所有的数据对指定的分组,每组都会产生一个迭代器,剩下你需要做的就是用数据条目构建有层次的数据结构。
使用xml或json,可以构建一个简单的对象并输出。
这部分的例子使用xml,提供了几个方便的方法处理结构化数据。
如果使用其他格式,例如自定义的格式,也要使用合适的构建对象的方法和序列化方法。
Consequences
输出是一种有层次的形式,根据指定的key分组的。
注意很多格式例如xmljson都有某种顶层根元素包在所有记录外面。
如果想让文档从根到底部都有良好的格式,也比较容易在特定的处理阶段加上头部或尾部。
Figure4-1.Thestructureofthestructuredtohierarchicalpattern
Knownuses
Pre-joiningdata
数据是杂乱的结构化数据集,为了分析,也很容易把数据组合成更复杂的对象。
通过这样,你设置好数据来充分利用分析nosql模型的优势。
PreparingdataforHBaseorMongoDB
Hbase是很自然的存储这类数据的方式。
所以可以用这种方法把数据搞到一起,作为加载到hbase或mongoDB的准备工作。
创建hbase表,然后通过MapReduce执行大量导入是很高效的。
另一种方案是分几次导入,可能效率较低。
Resemblances
Sql
RDB中这样的事情是很少见的,因为这样存储用sql分析不是很方便。
然而,这种方式可以解决RDBMS中的类似问题,比如做join然后在结果上做分析。
Pig
Pig对层次数据有适当的支持,可以取到层次的包和元组,然后就能容易的展现出层次结构和列出单条记录的对象。
Pig中的cogroup方法会保存原始结构做大量的合并数据的操作。
然而,使用这个关键词在复杂记录上做任何种类的实时分析都会有挑战性。
所以,自定义个方法是好的选择。
基本做法是使用pig创建并分组记录,然后用udf去分析数据。
data_a=LOAD'/data/comments/'ASPigStorage('|');
data_b=LOAD'/data/posts/'ASPigStorage(',');
grouped=COGROUPdata_aBY$2,data_bBY$1;
analyzed=FOREACHgroupedGENERATEudfs.analyze(group,$1,$2);
。
。
。
Performanceanalysis
使用这种模式时有两个性能关注点。
第一个,需要知道mapper发送到reducer的数据量,第二个,要清楚reducer创建的对象的内存使用情况。
因为要使用key分组的记录可以遍布在数据集的任何地方,很多数据会通过网络传输。
基于此原因,你需要注意使用适当的reducer的数量。
策略跟其它模式中的一样。
下一个主要关注的是数据的热点问题的可能性,它可以导致记录的暴增。
对大的数据集,一个特殊的输出记录会有很多数据与之关联是很可能出现的情况。
设想下stackOverflow上发了一个帖子,有一百万的评论。
这种情况相对少见,但不是不可能的。
如果你在创建某种xml对象,所有的评论某一时刻在输出之前都会存储在内存里。
这可能造成jvm的OOM,这种情况应该避免。
另一个热点问题是reducer处理的数据的倾斜问题。
这跟普通MapReducejob可能遇到的问题相似。
很多时候,倾斜问题可以忽略,如果倾斜很严重就写个自定义的partitioner使数据分发得更均匀。
StructuredtoHierarchicalExamples
Post/commentbuildingonStackOverflow
这个例子里,我们将拿到stackOverflow的发帖和评论数据并分组。
层次结构如下所示:
Posts
Post
Comment
Comment
Post
Comment
Comment
Comment
问题:
给出用户发帖和评论数据,创建结构化的xml层次结构,将相关的评论嵌套在对应的发帖之内。
Drivercode。
我们通常不会描述驱动代码,但这个例子中会加入新的东西:
MultipleInput。
创建这个对象,把评论数据和发帖数据的路径加到指定的mapper。
这两个路径是通过命令行提供的,通过args数组获得。
publicstaticvoidmain(String[]args)throwsException{
Configurationconf=newConfiguration();
Jobjob=newJob(conf,"PostCommentHierarchy");
job.setJarByClass(PostCommentBuildingDriver.class);
MultipleInputs.addInputPath(job,newPath(args[0]),
TextInputFormat.class,PostMapper.class);
MultipleInputs.addInputPath(job,newPath(args[1]),
TextInputFormat.class,CommentMapper.class);
job.setReducerClass(UserJoinReducer.class);
job.setOutputFormatClass(TextOutputFormat.class);
TextOutputFormat.setOutputPath(job,newPath(args[2]));
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
System.exit(job.waitForCompletion(true)?
0:
2);
}
Mappercode。
这里有两个mapper类,分别是评论和发帖类。
两者都用发帖id作为输出key。
输出值用字符(p代表发帖,c代表评论),我们在reduce阶段就会知道数据来自哪里。
publicstaticclassPostMapperextendsMapper
privateTextoutkey=newText();
privateTextoutvalue=newText();
publicvoidmap(Objectkey,Textvalue,Contextcontext)
throwsIOException,InterruptedException{
Map
.toString());
//TheforeignjoinkeyisthepostID
outkey.set(parsed.get("Id"));
//Flagthisrecordforthereducerandthenoutput
outvalue.set("P"+value.toString());
context.write(outkey,outvalue);
}
}
publicstaticclassCommentMapperextendsMapper
privateTextoutkey=newText();
privateTextoutvalue=newText();
publicvoidmap(Objectkey,Textvalue,Contextcontext)
throwsIOException,InterruptedException{
Map
//TheforeignjoinkeyisthepostID
outkey.set(parsed.get("PostId"));
//Flagthisrecordforthereducerandthenoutput
outvalue.set("C"+value.toString());
context.write(outkey,outvalue);
}
}
Reducercode。
Reducer创建有层次的xml对象。
所有的值被迭代,来得到发帖记录和对应的评论列表。
我们知道根据标识分别记录并加到值里。
标识在指派为帖子(对应为帖子)或加到list(对应为评论)时移除掉。
如果帖子不为空,就按帖子为父节点,评论为子节点创建xml记录。
紧随其后的是nestElements。
使用xmlapi创建xml记录,你可以在需要时尽情使用。
publicstaticclassPostCommentHierarchyReducerextends
Reducer
privateArrayList
privateDocumentBuilderFactorydbf=DocumentBuilderFactory
.newInstance();
privateStringpost=null;
publicvoidreduce(Textkey,Iterable
throwsIOException,InterruptedException{
//Resetvariables
post=null;
comments.clear();
//Foreachinputvalue
for(Textt:
values){
//Ifthisisthepostrecord,storeit,minustheflag
if(t.charAt(0)=='P'){
post=t.toString().substring(1,t.toString().length())
.trim();
}else{
//Else,itisacommentrecord.Addittothelist,minus
//theflag
comments.add(t.toString()
.substring(1,t.toString().length()).trim());
}
}
//Iftherearenocomments,thecommentslistwillsimplybeempty.
//Ifpostisnotnull,combinepostwithitscomments.
if(post!
=null){
//nestthecommentsunderneaththepostelement
StringpostWithCommentChildren=nestElements(post,comments);
//writeouttheXML
context.write(newText(postWithCommentChildren),
NullWritable.get());
}
}
nestElements方法拿到帖子和评论的列表,创建一个xml字符串并输出。
使用DocumentBuilder和一些额外的帮助方法拷贝Element对象为新的,还有他们的属性。
这种拷贝发生在数据行转为帖子或评论时的重命名标签时。
最终的document被转换成了xml。
privateStringnestElements(Stringpost,List
//CreatethenewdocumenttobuildtheXML
DocumentBuilderbldr=dbf.newDocumentBuilder();
Documentdoc=bldr.newDocument();
//Copyparentnodetodocument
ElementpostEl=getXmlElementFromString(post);
ElementtoAddPostEl=doc.createElement("post");
//Copytheattributesoftheoriginalpostelementtothenewone
copyAttributesToElement(postEl.getAttributes(),toAddPostEl);
//Foreachcomment,copyittothe"post"node
for(StringcommentXml:
comments){
ElementcommentEl=getXmlElementFromString(commentXml);
ElementtoAddCommentEl=doc.createElement("comments");
//Copytheattributesoftheoriginalcommentelementto
//thenewone
copyAttributesToElement(commentEl.getAttributes(),
toAddCommentEl);
//Addthecopiedcommenttothepostelement
toAddPostEl.appendChild(toAddCommentEl);
}
//Addthepostelementtothedocument
doc.appendChild(toAddPostEl);
//TransformthedocumentintoaStringofXMLandreturn
returntransformDocumentToString(doc);
}
privateElementgetXmlElementFromString(Stringxml){
//Createanewdocumentbuilder
DocumentBuilderbldr=dbf.newDocumentBuilder();
returnbldr.parse(newInputSource(newStringReader(xml)))
.getDocumentElement();
}
privatevoidcopyAttributesToElement(NamedNodeMapattributes,
Elementelement){
//Foreachattribute,copyittotheelement
for(inti=0;i AttrtoCopy=(Attr)attributes.item(i); element.setAttribute(toCopy.getName(),toCopy.getValue()); } } privateStringtransformDocumentToString(Documentdoc){ TransformerFactorytf=TransformerFactory.newInstance(); Transformertransformer=tf.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); StringWriterwriter=newStringWriter(); transformer.transform(newDOMSource(doc),newStreamResult(writer)); //Replaceallnewlinecharacterswithanemptystringtohave //onerecordperline. returnwriter.getBuffer().toString().replaceAll("\n|\r",""); } Question/answerbuildingonStackOverflow 本例是前面例子的后续处理,会使用前面例子的输出作为本例的输入数据。 现在,我们已经得到了帖子和跟帖子关联的评论,现在我们要关联帖子的提问和帖子的回答。 这是需要做的,因为帖子由回答帖和提问帖构成,并根据postTypeId区分。 我们将根据提问的id和回答的父id分组到一起。 问题: 使用前面例子的输出,执行自关联操作,创建问题帖,回答帖,和评论的层次结构。 Mappercode。 首先决定记录是问题还是回答,因为这两种记录的行为不同。 对于问题,抽取它的id作为key,并标记为问题。 对于回答,抽取父id作为key,并标记为回答。 publicclassQuestionAnswerBuildingDriver{ publicstaticclassPostCommentMapperextends Mapper privateDocumentBuilderFactorydbf=DocumentBuilderFactory .newInstance(); privateTextoutkey=newText(); privateTextoutvalue=newText(); publicvoidmap(Objectkey,Textvalue,Contextcontext) throwsIOException,InterruptedException{ //Parsethepost/commentXMLhierarc
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- chapter4MapReduceandDesignPatterns
![提示](https://static.bdocx.com/images/bang_tan.gif)