Hadoop SequenceFile:只处理一定大小的键/值



我有一个序列文件,包含~ 12,000个键/值对。最大的值是143MB,其他值小于1MB。"mapred.child.java.opts"设置为"-Xmx500m"。当处理这个文件时,我得到一个Error: Java heap space。这是我的Mapper:

public class MyMapper extends Mapper<Text, BytesWritable, Text, Text> {
    @Override
    protected void setup(Context context) throws IOException, InterruptedException {
        super.setup(context);
    }
    @Override
    protected void map(Text key, BytesWritable value, Context context) throws IOException, InterruptedException {
        Configuration conf = context.getConfiguration();
        String content = new String(value.getBytes());
        // remove HTML tags (content is a HTML document)
        String content_no_html = Jsoup.parse(content).text();
        // try to find a regex match
        }
    }    
}

我认为这可能是因为我的Map类正在将值从BytesWritable转换为String,然后处理该字符串。但是我试图只处理映射器中的键/值对,如果value.getLength() < 8388608 (8 MB),仍然内存不足。

我可以增加-Xmx500m,但我担心这不会解决我的问题,因为我有几千个序列文件,我需要处理,我不知道最大的键/值对有多大(我对大键/值对不感兴趣,因此上面的8MB限制)。为了记录,我尝试了一些序列文件,导致~2500个映射步骤,并且在第一个1990左右成功后发生错误,这使我认为单个键/值对大小是这里的问题。

是否有一种方法只传递键/值对到映射器的大小限制?为什么我一开始就得到Error: Java heap space ?

编辑

我尝试按照建议创建自定义SequenceFileInputFormat,并发现在调用映射函数之前发生堆空间错误。确切地说,SequenceFileRecordReaders nextKeyValue()是导致错误的原因。我再次检查了地图功能,但它什么都没做,还是崩溃了。我找不到解决错误的方法,所以我想增加内存是我唯一的机会。

8 MB的HTML还是太大了!

作为一个例子,让我们考虑您的8 MB html文本是一个包含80字节长的元素的ul列表,您将有1,000,000个元素:

<ul>
    <li class="item" data-index="000000">This is the <b>first</b> line.</li>
    <li class="item" data-index="000001">This is the <b>second</b> line.</li>
    [...]
    <li class="item" data-index="999999">This is the <b>last</b> line.</li>
</ul>
每个li元素的Jsoup Element类(它是Node的子类)具有以下结构,从中我们可以估计(理论上和近似地)我的示例html列表的内存占用:
  • 元素。tag:对tag对象的引用(其中1字符串+ 8布尔值)=> 64字节
  • 元素。classNames: Set of Class names => 56 bytes
  • 节点。parentNode: Reference to Node => 8 bytes
  • 节点。attributes:对attributes对象的引用=> 152字节
    • 包含一个LinkedHashMap<String, Attribute>和一个条目(48字节)
      • 属性包含2个字符串(~56*2=112字节)
  • 节点。childNodes:节点列表
    • 3 TextNodes(因为b标签)=>大约。每个100字节(文本内容+节点字段,这是内存开始!)

所以列表中单个元素所使用的内存大约是580字节。

这1,000,000个元素使用的总内存约为580mb。

同样,主ul元素包含了一个包含1,000,000个引用的列表,这些引用来自于li元素在它的childrenNodes字段中,所以它占用了8mb的内存。

最后,当调用text()时,如果删除标记后留下的文本对应于5MB的序列化字符,那么您需要一个500万char s的String,占用5,000,000*2 = 10 MB。

如果你的html就像我展示的列表,那么你需要在你的堆中存储,在调用map()函数的时候:

  • new String(value.getBytes()) => ~ 16mb
  • Jsoup.parse(content) => ~ 590mb
  • .text() => ~ 10mb

难怪你得到一个java堆空间错误,堆只有500 MB !

使用JSoup解析文件时会发生"内存爆炸",因为它必须创建大量对象,而java对象,特别是字符串,非常重。

这完全取决于你的html看起来像什么。如果你的html有大约相同的html标签/属性的浓度作为我的例子,我认为你可以定义你的阈值约2 MB是安全的。

您可以在value.getLength() < 2_000_000开始时退出映射函数,就像您所做的那样。或者,如果不浪费数据对您来说很重要,您可以做一些更花哨的事情,比如运行一个regex来计算content字符串中的标记或属性的数量。如果你有20mb的文本,但里面只有一个标签,它仍然适合内存,不需要跳过它

相关内容

  • 没有找到相关文章

最新更新