HUGE XML文件存在问题

时间:2010-08-06 17:10:37

标签: c# xml file performance large-files

我有16个大型xml文件。当我说Large时,我说的是千兆字节。其中一个文件超过8 GB。其中一些超过1 GB。这些是从外部提供商处获得的。

我正在尝试将XML导入数据库,以便将其粉碎成表格。目前,我一次将10,000条记录从文件中流入内存并插入blob。我使用SSIS执行脚本任务。对于除8 GB文件之外的所有文件,这实际上非常快。

我无法将整个文件加载到xml文档中。我不能强调这一点。这是迭代1,文件非常庞大,系统只是试图处理这些文件,特别是8 GB的文件。

我运行了当前的“文件分割器”,它花了7个小时导入xml数据但仍然没有完成。它从8 GB文件中导入了363个10,000个记录的块,但仍未完成。

仅供参考,以下是我目前将文件传输到内存中的方式(一次10,000条记录)。我在http://blogs.msdn.com/b/xmlteam/archive/2007/03/24/streaming-with-linq-to-xml-part-2.aspx

找到了代码
private static IEnumerable<XElement> SimpleStreamAxis(string fileName, string matchName) 
        {
            using (FileStream stream = File.OpenRead(fileName))
            {
                using (XmlReader reader = XmlReader.Create(stream, new XmlReaderSettings() { ProhibitDtd = false }))
                {
                    reader.MoveToContent();
                    while (reader.Read())
                    {
                        switch (reader.NodeType)
                        {
                            case XmlNodeType.Element:
                                if (reader.Name == matchName)
                                {
                                    XElement el = XElement.ReadFrom(reader) as XElement;
                                    if (el != null)
                                        yield return el;
                                }
                                break;
                        }
                    }

                    reader.Close();
                }

                stream.Close();
            }
        }

因此,它可以在所有文件上正常工作,除了8 GB之外,因为它必须进一步流入文件所需的时间越来越长。

我想要做的是将文件拆分成更小的块,但拆分器需要快速。然后流光和其余的流程可以更快地运行。分割文件的最佳方法是什么?理想情况下,我自己将其拆分为SSIS中的代码。

编辑:

以下是使用流式传输方法实际分页我的数据的代码。

connection = (SqlConnection)cm.AcquireConnection(null);

                int maximumCount = Convert.ToInt32(Dts.Variables["MaximumProductsPerFile"].Value);
                int minMBSize = Convert.ToInt32(Dts.Variables["MinimumMBSize"].Value);
                int maxMBSize = Convert.ToInt32(Dts.Variables["MaximumMBSize"].Value);

                string fileName = Dts.Variables["XmlFileName"].Value.ToString();

                FileInfo info = new FileInfo(fileName);

                long fileMBSize = info.Length / 1048576; //1024 * 1024 bytes in a MB

                if (minMBSize <= fileMBSize && maxMBSize >= fileMBSize)
                {
                    int pageSize = 10000;     //do 2000 products at one time

                    if (maximumCount != 0)
                        pageSize = maximumCount;

                    var page = (from p in SimpleStreamAxis(fileName, "product") select p).Take(pageSize);
                    int current = 0;

                    while (page.Count() > 0)
                    {
                        XElement xml = new XElement("catalog",
                            from p in page
                            select p);

                        SubmitXml(connection, fileName, xml.ToString());

                        //if the maximum count is set, only load the maximum (in one page)
                        if (maximumCount != 0)
                            break;

                        current++;
                        page = (from p in SimpleStreamAxis(fileName, "product") select p).Skip(current * pageSize).Take(pageSize);
                    }
                }

5 个答案:

答案 0 :(得分:5)

每次使用from p in SimpleStreamAxis位重新读取并扫描到文件中时,您似乎都在重复读取XML文件的每一步。同样通过调用Count(),您每次都会走完整页。

尝试这样的事情:

var full = (from p in SimpleStreamAxis(fileName, "product") select p);
int current = 0;

while (full.Any() > 0)
{
    var page = full.Take(pageSize);

    XElement xml = new XElement("catalog",
    from p in page
    select p);

    SubmitXml(connection, fileName, xml.ToString());

    //if the maximum count is set, only load the maximum (in one page)
    if (maximumCount != 0)
        break;

    current++;
    full = full.Skip(pageSize);
}

请注意,这是未经测试的,但您应该希望得到这个想法。你需要避免多次枚举文件,像Count()和Take / Skip这样的操作在8gb xml文件上需要很长时间。

更新:我认为上面的内容仍然会比我们想要的更多次遍历文件,你需要更加可预测的东西:

var full = (from p in SimpleStreamAxis(fileName, "product") select p);
int current = 0;

XElement xml = new XElement("catalog");
int pageIndex = 0;

foreach (var element in full)
{
    xml.Add(element);

    pageIndex++;
    if (pageIndex == pageSize)
    {
        SubmitXml(connection, fileName, xml.ToString());
        xml = new XElement("catalog");
        pageIndex = 0;
    }

    //if the maximum count is set, only load the maximum (in one page)
    if (maximumCount != 0)
        break;

    current++;
}

    // Submit the remainder
if (xml.Elements().Any())
{
    SubmitXml(connection, fileName, xml.ToString());
}

答案 1 :(得分:4)

您将需要一个SAXReader来处理大型XML文件。

答案 2 :(得分:2)

您是否考虑过使用SAX解析器?没有一个由微软发布,但网上有一些例子。使用SAX解析器,您基本上可以将文件作为流和事件激发,您可以监听,并将整个事物加载到内存中,而您显然无法做到这一点。我不太了解使用SAX解析器,所以我没有具体的建议,但是很多Java人员多年来一直以这种方式完成XML。

答案 3 :(得分:2)

如果您正在使用MS SQL Server,请使用XML Bulk Load来实现此目的。
Knowledgebase Article

答案 4 :(得分:0)

看一下这个项目,它将XML文件拆分成较小的文件来解决你的问题:

将大型XML文件拆分为小文件: http://www.codeproject.com/KB/XML/SplitLargeXMLintoSmallFil.aspx