我有一个XML文件,其压缩大小约为100 GB(未压缩的1 TB)。该文件以下列方式包含大约1亿个条目:
<root>
<entry>
<id>1234</id>
...
</entry>
<entry>
<id>1230</id>
...
</entry
</root>
我想按ID排序此文件。这样做的好方法是什么?
顺便说一下,我可以使用16核和128 GB RAM的机器。
答案 0 :(得分:0)
您可以考虑使用像Saxon http://www.saxonica.com/html/documentation/sourcedocs/streaming/这样的流处理器,并使用XSLT进行排序。
另一个选项可能是将数据存储为密钥,DB中的值,使用SQL对它们进行排序并重新创建XML。您将利用数据库的强大功能来管理大量数据。
类似的问题(不一样):Sort multigigabyte xml file
答案 1 :(得分:0)
因为值(即 id )是自然数,所以对它们进行排序的最佳算法是 计数排序 TETA(n)时间顺序。
假设值在[1 .. k]
范围内计算排序&gt;
温度: C [1..k]
输入: A [1..n]
输出: B [1..n]
CountingSort (A, B, k)
{
for(i=1 to k) C[i]=0;
for(i=1 to n) C[A[i]]++;
for(i=2 to k) C[i]=C[i]+C[i-1];
for(i=n downto 1)
{
B[C[A[i]]] = A[i];
C[A[i]]--;
}
}
此算法稳定。
您也可以使用相同的订单基数排序。
答案 2 :(得分:0)
在这个阶段,记住人们在数据比可用直接存取存储器大得多的时候用来对磁带或穿孔卡片组进行分类的技术是有用的。 (我曾经看过一组运营商对25万张卡片进行排序 - 大约120个托盘)。您基本上需要流式传输,合并和拆分的组合,这些都是原则上使用XSLT 3.0提供的所有操作。有两种处理器,Saxon-EE和Exselt,它们都不是100%完整的实现,因此您将受限于产品的限制而不是规范。
我的直觉是进行逐位排序。你没有说id被用作排序键的时间有多长。这里的“数字”当然不一定是十进制数字,但为了简单起见假设为十进制,基本思路是你首先根据排序键的最后一位将文件拆分成10个桶,然后你处理桶中的基于这种排序的顺序,这次按倒数第二位排序,并按密钥中的数字进行一次运算:排序键中每个数字的完整文件的一次传递。
如果id是密集的,那么大概是100m键,它们大约是8位数,那将是8次通过,如果我们假设处理速度为10Gb / min,这可能是你可以得到的最好的 - 架式XML解析器,然后1Tb文件的每次传递将花费2个小时,因此8个传递将是16个小时。但是使用say base-100可能会更好,所以你在每次传球时分成100个文件,然后你只有4次传球。
基本的XSLT 3.0代码是:
<xsl:stream href="in.xml">
<xsl:fork>
<xsl:for-each-group select="record"
group-by="substring(key, $digit, 1)">
<xsl:result-document href="temp{current-grouping-key()}">
<xsl:sequence select="current-group()"/>
</xsl:result-document>
</xsl:for-each-group>
</xsl:fork>
现在是坏消息:在Saxon-EE 9.7中,这段代码可能没有得到足够的优化。虽然原则上每个组中的项目可以直接流式传输到相关的序列化结果文档,但Saxon还没有特别处理这个案例,并且会在处理它之前在内存中构建每个组。我不知道Exselt能否做得更好。
还有其他选择吗?好吧,也许我们可以尝试这样的事情:
我认为这在撒克逊会有用。第一步可以使用在Saxon中完全流式传输的<xsl:for-each-group group-adjacent="(position()-1) idiv $N">
来完成。
这实际上是一个3遍解决方案,因为每个项目都被解析和序列化三次。我会去将1Tb文件拆分成100个10Gb文件。做一个10Gb的内存XSLT正在推动它,但你有一些马力可以玩。但是,你可能会遇到Java寻址限制:我认为数组和字符串有1G的限制。