﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-ricdong</title><link>http://www.blogjava.net/ricdong/</link><description /><language>zh-cn</language><lastBuildDate>Wed, 29 Apr 2026 08:46:32 GMT</lastBuildDate><pubDate>Wed, 29 Apr 2026 08:46:32 GMT</pubDate><ttl>60</ttl><item><title>MapReduce 数据分布倾斜性</title><link>http://www.blogjava.net/ricdong/articles/366991.html</link><dc:creator>Ric Dong</dc:creator><author>Ric Dong</author><pubDate>Thu, 22 Dec 2011 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/ricdong/articles/366991.html</guid><wfw:comment>http://www.blogjava.net/ricdong/comments/366991.html</wfw:comment><comments>http://www.blogjava.net/ricdong/articles/366991.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ricdong/comments/commentRss/366991.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ricdong/services/trackbacks/366991.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25px; background-color: #ffffff; "><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-family: 宋体; "><span style="font-size: small; ">数据分布倾斜性指的是数据分布过度集中于数据空间的某端，造成&#8220;头重脚轻&#8221;或者&#8220;比萨斜塔&#8221;等不均匀的分布特点。数据分布倾斜性将造成运算效率上的&#8220;瓶颈&#8221;和数据分析结果的&#8220;以偏概全&#8221;。</span></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><br /></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><span style="font-family: 宋体; "><strong style="font-weight: bold; ">效率上的&#8220;瓶颈&#8221;</strong></span><span lang="EN-US"></span></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><span style="font-family: 宋体; ">假如在大型商场中，共有</span><span lang="EN-US">A,B1,B2</span><span style="font-family: 宋体; ">&#8230;</span><span lang="EN-US">..B9</span><span style="font-family: 宋体; ">十家店铺，其中</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">店铺中有</span><span lang="EN-US">99W</span><span style="font-family: 宋体; ">商品，</span><span lang="EN-US">B1,B2</span><span style="font-family: 宋体; ">&#8230;</span><span lang="EN-US">.B9</span><span style="font-family: 宋体; ">这九家店铺分别有</span><span lang="EN-US">1W</span><span style="font-family: 宋体; ">商品。我们要统计商场中商品总数，计算初，采用</span><span lang="EN-US">HASHMAP</span><span style="font-family: 宋体; ">作为存储结构，其中</span><span lang="EN-US">Key</span><span style="font-family: 宋体; ">：店铺</span><span lang="EN-US">&nbsp;Value</span><span style="font-family: 宋体; ">：商品。我们的计算过程是先统计每个店铺的商品总数，最后将结果累加。可以发现，由于</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">有</span><span lang="EN-US">99W</span><span style="font-family: 宋体; ">商品，按照</span><span lang="EN-US">1+1</span><span style="font-family: 宋体; ">的累积方式（假如</span><span lang="EN-US">1+1</span><span style="font-family: 宋体; ">耗时</span><span lang="EN-US">1</span><span style="font-family: 宋体; ">秒），我们要加</span><span lang="EN-US">99W</span><span style="font-family: 宋体; ">个</span><span lang="EN-US">1</span><span style="font-family: 宋体; ">才能得到</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">店铺的商品总数（总耗时</span><span lang="EN-US">99W</span><span style="font-family: 宋体; ">秒），而</span><span lang="EN-US">B1,B2</span><span style="font-family: 宋体; ">&#8230;</span><span lang="EN-US">.B9</span><span style="font-family: 宋体; ">只需分别累加</span><span lang="EN-US">1W</span><span style="font-family: 宋体; ">个</span><span lang="EN-US">1</span><span style="font-family: 宋体; ">（分别耗时</span><span lang="EN-US">1W</span><span style="font-family: 宋体; ">秒），而为了得到商场中的商品总数，我们必须等待所有店铺都分别累计结束才能处理总和，显而易见，此时运算瓶颈便集中在</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">店铺的商品累计上。</span><span lang="EN-US"></span></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><span style="font-family: 宋体; ">这类状况经常发生在分布式运算过程中，比如</span><span lang="EN-US">Hadoop Job</span><span style="font-family: 宋体; ">计算，因为</span><span lang="EN-US">map/reduce&nbsp;</span><span style="font-family: 宋体; ">过程中是以</span><span lang="EN-US">Key-value</span><span style="font-family: 宋体; ">形式来处理数据，假如某</span><span lang="EN-US">key</span><span style="font-family: 宋体; ">下的数据量太大，会导致整个计算过程中</span><span lang="EN-US">move/shuffle/sort</span><span style="font-family: 宋体; ">的耗时远远高于其他</span><span lang="EN-US">key</span><span style="font-family: 宋体; ">，因此该</span><span lang="EN-US">Key</span><span style="font-family: 宋体; ">变成为效率&#8220;瓶颈&#8221;。一般解决办法是，自定义</span><span lang="EN-US">partitioner</span><span style="font-family: 宋体; ">，对所有的</span><span lang="EN-US">Value</span><span style="font-family: 宋体; ">进行自定义分组，使得每组的量较平均，从而解决时间瓶颈问题。</span></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><br /></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><span style="font-family: 宋体; "><strong style="font-weight: bold; ">数据分析结果的&#8220;以偏概全&#8221;</strong></span><span lang="EN-US"></span></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><span style="font-family: 宋体; ">同样使用上述的&#8220;商场&#8221;案例，并且在此基础上我们假设</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">店铺</span><span lang="EN-US">,B9</span><span style="font-family: 宋体; ">店铺是卖低端商品，而</span><span lang="EN-US">B1,B2</span><span style="font-family: 宋体; ">&#8230;</span><span lang="EN-US">..B8</span><span style="font-family: 宋体; ">是卖高端商品，销量较小。如果我们要根据商品销售状况分析店铺在买家当中的受欢迎程度。由于</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">店铺本身商品量大，而且定位的销售价位是属于薄利多销，如果只从销售量的考虑，我们会以为</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">店铺在商场中是最受买家欢迎的，造成&#8220;片面&#8221;的分析结果。</span><span lang="EN-US"></span></span></p><p class="MsoNormal" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span style="font-size: small; "><span style="font-family: 宋体; ">其实，遇到这种情况，我们首先的分析卖家性质和买家性质，并且使用相对量来作为评估值，比如</span><span lang="EN-US">A</span><span style="font-family: 宋体; ">店铺卖低端商品，日销售量</span><span lang="EN-US">1W</span><span style="font-family: 宋体; ">商品，</span><span lang="EN-US">1W/99W&lt;1%,&nbsp;</span><span style="font-family: 宋体; ">而</span><span lang="EN-US">B9</span><span style="font-family: 宋体; ">店铺卖低端商品，日销售量</span><span lang="EN-US">5K</span><span style="font-family: 宋体; ">商品，</span><span lang="EN-US">5K/1W=50%,</span><span style="font-family: 宋体; ">所以在低端买家中，低端商品店铺</span><span lang="EN-US">B9</span><span style="font-family: 宋体; ">应该是最受欢迎的。</span></span></p></span><img src ="http://www.blogjava.net/ricdong/aggbug/366991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ricdong/" target="_blank">Ric Dong</a> 2011-12-22 10:17 <a href="http://www.blogjava.net/ricdong/articles/366991.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MapReduce 解析XML算法的一点构思</title><link>http://www.blogjava.net/ricdong/articles/366960.html</link><dc:creator>Ric Dong</dc:creator><author>Ric Dong</author><pubDate>Wed, 21 Dec 2011 13:15:00 GMT</pubDate><guid>http://www.blogjava.net/ricdong/articles/366960.html</guid><wfw:comment>http://www.blogjava.net/ricdong/comments/366960.html</wfw:comment><comments>http://www.blogjava.net/ricdong/articles/366960.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ricdong/comments/commentRss/366960.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ricdong/services/trackbacks/366960.html</trackback:ping><description><![CDATA[<div><div>没想到Hadoop在解析XML时如此纠结，以至于新版api的mapreduce竟然放弃了XML格式的format以及reader，在老版（hadoop-0.19.*）的streaming模块提供了这样的api，由于我用的hadoop-0.20.2 3U1版本，因此需要把处理XML的几个类移植过来使用。</div><div>&nbsp;</div><div>移植所带来的问题是各处依赖包，和各种api不兼容。没关系，我可以看一下源码，然后自己写一个。细看了一下reader的代码，发现mapreduce使用了BufferedInputStream的mark，reset来寻找XML的tag，这个tag就是我们在提交作业所设置的，比如&lt;log&gt;，&lt;/log&gt;这样的标签。Java中stream流的mark和reset，允许指针回读，即在找到&lt;log&gt;时，mark一下指针，然后再找到&lt;/log&gt;标签，最后通过reset方法，返回到mark的位置，把&lt;log&gt;&lt;/log&gt;内的数据读取出来。但在匹配的过程中，我发现mapred使用了BufferedInputStream 的 read(); 方法，该方法返回下一个可读的字节。那么整个处理过程就是读一个字节，比较一个字节，我没有在mapreduce中用这样的算法，但我测试过，向缓冲区（BufferedInputStream）中一个字节一个字节的读，性能严重不足，read(); 方法平均返回时间在331纳秒，处理一个170M的xml文档（tag比较多），竟然花了200+秒。（streaming模块还写了一个faster*方法，哎，慢死了）</div><div>&nbsp;</div><div>周敏同学提供了pig中处理xml的reader，但pig那边的代码我还没细看，也不知道hadoop的jira中有没有新的feature来解决现有xml的问题。如果有的话，不防可以告诉我一下下。呵呵。&nbsp;</div><div>&nbsp;</div><div>现在有一个构思，即主要思想仍然围绕字节比较，因为字符串匹配效率更低，另外算法源于String.indexOf(&#8220;&#8221;)，即找到&lt;log&gt;这个后，记住位置，然后再找&lt;/log&gt;，这样算完全匹配，中间的内容用system.arraycopy来复制到新的字节数组，目前这算法我实现了一半，即找到&lt;log&gt;和&lt;/log&gt;后，把这两个签标全部替换掉，170M文档，用时2.2秒（最快1.3秒）。</div><div>&nbsp;</div><div>算法及问题：</div><div>首先提供一个BufferedInputStream，默认大小8k，在程序中建一个字节数组，大小为4k，即每次向BufferedInputStream读4k，这个效率是很不错的，然后去寻找&lt;log&gt;.toArray这样的字节数组，这一步速度是很惊人的。但这里有一个小的问题，即每次读4k的大小去处理，那很有可能&lt;log&gt;&lt;/log&gt;位于两次读取的一尾一头，那么我的想法是做一个半循环的字节数组，即如果在4k的字节数组中的最后找到&lt;log&gt;，那么就把前面未匹配的仍掉，然后把&lt;log&gt;标签移到字节数组最前端，然后另用这个字节数组再向BufferedInputStream中去读4k-5长度的内容（5是&lt;log&gt;的字节长度）。关于4k这个大小，首先要对XML数据进行sampling，即确定&lt;log&gt;&lt;/log&gt;当中的内容长度，然后再定这个缓冲buf的大小。</div></div><img src ="http://www.blogjava.net/ricdong/aggbug/366960.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ricdong/" target="_blank">Ric Dong</a> 2011-12-21 21:15 <a href="http://www.blogjava.net/ricdong/articles/366960.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>