构建和导出具有有限内存的非常大的树结构

时间:2013-11-20 20:08:59

标签: java tree

总而言之,我有一个应用程序,它接受一组输入文件,从数据中生成一个树,然后将其作为XML写入文本文件。

目前,整个树在写出之前都存储在内存中,因为在解析过程中我们需要引用树上的任意节点来获取或更新其值。

当树变得太大而无法将所有内容存储在内存中时,我们遇到的问题就出现了。树本身非常平坦,只有4-6级深度。它看起来像这样

Root
   Group
      Record
         Data
         Data
      Record
         Data
         Data
         Data
         ...
      ...
   Group
      Record
      ...

总会有一个Root节点,每个节点只有一种类型的子节点。但是,节点添加到其他节点的方式也没有顺序:根据数据的格式,您可以将记录添加到不同的组,并且可以将数据添加到不同的记录(而不是为一个记录添加一个记录)小组,然后继续前进到另一个)

我的第一个建议就是在机器上投入更多内存。我们在64位Windows机器上运行该工具,所以如果我们的内存不足,那么我们只需要获得更多内存。但是没有采纳这个建议。

我的下一个想法是每当树占用太多内存空间时写出节点,但由于数据可以随时添加到特定记录中,因此很难确定我们何时完成记录与否。特别是如果我们需要回访一个记录,它已经被写出来了。

还有其他几个选项,例如优化树的设计方式(因为每个节点占用相当大的内存),但对于这个问题,我想知道构建和导出大树的技术。

4 个答案:

答案 0 :(得分:3)

在我看来,有两种方法可以解决这个问题。

  • 详细了解数据的使用情况,以确定具体的模式,并为您的用例提供最有效的方法。

  • 将数据视为黑框,假设任何访问或更改数据的方案都可能以相同的频率和概率进行。

你没有给我们任何第一种方法的食物,所以我们必须假设后者。缓存的概念作为一种解决方案浮现在脑海中。有不同类型的缓存,但基本概念是你尽可能地保留在内存中,一旦你超过一定的限制,你就会持久存储并从内存中删除使用次数最少或时间最长的部分。 / p>

执行此操作时,您可以选择将实际树结构保留在内存中,仅清除节点内容或清除节点内容和树结构本身。如果节点数量很大但数量有限,那么保持树结构使“清除”节点尽可能轻量化可能会更好。但是,如果树中的节点数实际上是无限的,那么您可以考虑清除整个子树。

当树访问通常通过访问子树而不是完全随机的方式完成时,最后一种方法对于用例非常有效。

如果您提供有关数据和使用模式的更多信息,我们可能会提出更详细的建议。

答案 1 :(得分:3)

1)我想到的第一个选择是将所有(父子)对存储在数据库中,然后递归地探索它以从中构建XML。

2)另一种选择是自下而上,通过扫描完整输入三次(每层一次,从记录作为父项开始,以root结尾)。每个层都作为一组XML文件存储在磁盘中,每个节点一个。然后,当在树中构建更高级别时,可以将子文件简单地附加到其对应的父文件(因为它们保证完全填充)。这需要维护2个内存索引;一个用于当前级别,一个用于其下一个级别。这些索引指向文件。

答案 2 :(得分:3)

  1. 在磁盘上创建工作目录。

  2. 通读数据。当您遇到之前没有见过的组时,请在工作目录中为它创建一个子目录。当您看到之前未见过的记录时,请在相应的组目录中为其创建一个文件。当您看到某些数据时,请将其附加到相应的文件中。继续阅读,直到您完成数据阅读。

  3. 遍历文件树,并将记录文件的内容连接到输出文件,并为根和组提供相应的标记。

  4. 删除工作目录及其中的所有内容

  5. 如果你坚持使用内存中的文件句柄,并在写入和阅读阶段之间重复使用它们(这意味着要使用RandomAccessFileMappedByteBuffer,这两者都可以写入和读取并且,并且不要在任何时候刷新它们,那么你将把磁盘IO和缓存的问题完全留在操作系统的手中(好吧,运行时库等等)。如果操作系统决定该程序的特定执行的最佳方法是将一些数据写入磁盘,它将执行此操作。如果它可以全部适合内存,它将把它全部保存在内存中。它将能够批量写入,因此它们既美观又大,因此效率高。它将能够预取读取,这些读取是一组文件的顺序遍历,因此是可预测的。如果您的操作系统是好的,这将是一个有效的解决方案,因为这个问题承认。如果它是Windows,它可能不是。

    额外的技巧是将数据写入不是XML的文件,而是以更紧凑的中间格式,只将其转换为XML以进行最终输出。这样可以更有效地利用缓存和带宽。

答案 3 :(得分:2)

如果我是你,我会在处理它时坚持树,而不是将它存储在内存中。通过持久性,我的意思是从平面文本文件到关系数据库到面向图形的NoSQL解决方案。

然后,您可以以Java bean的形式为您需要执行的CRUD操作创建一个基本访问层,然后就可以了。

另一种选择是动态构建XML文件,从而跳过内存中树的构建。为此,您可以利用SAX,它是基于事件的XML处理解决方案,而不是将整个文档加载到内存中。