Проблемы с ОГРОМНЫМИ XML-файлами - PullRequest
6 голосов
/ 06 августа 2010

У меня 16 больших XML-файлов. Когда я говорю «Большой», я говорю в гигабайтах. Один из этих файлов более 8 ГБ. Некоторые из них более 1 ГБ. Они даны мне от внешнего поставщика.

Я пытаюсь импортировать XML в базу данных, чтобы разбить его на таблицы. В настоящее время я передаю 10000 записей за раз из файла в память и вставляю большой двоичный объект. Я использую SSIS с задачей сценария, чтобы сделать это. На самом деле это ОЧЕНЬ быстро для всех файлов, кроме файла 8 ГБ.

Я не могу загрузить весь файл в XML-документ. Я не могу подчеркнуть это достаточно. Это была итерация 1, и файлы настолько огромны, что система просто зависает, пытаясь обработать эти файлы, в частности, 8-гигабайтный.

Я запустил свой текущий «разделитель файлов», и он потратил 7 часов на импорт данных XML, но все еще не закончил. Он импортировал 363 блока по 10000 записей из файла размером 8 ГБ, но все еще не был готов.

К вашему сведению, вот как я сейчас передаю свои файлы в память (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 ГБ, где при дальнейшей и дальнейшей потоковой передаче в файл требуется больше и больше времени.

Я хотел бы разбить файл на более мелкие куски, но сплиттер должен быть быстрым. Тогда стример и остальная часть процесса могут работать быстрее. Каков наилучший способ разделения файлов? В идеале я бы сам разбил его на код в SSIS.

EDIT:

Вот код, который фактически выводит мои данные с использованием методологии потоковой передачи.

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 ]

5 голосов
/ 06 августа 2010

Похоже, что вы перечитываете в файл XML снова и снова каждый шаг, каждый раз, когда вы используете бит from p in SimpleStreamAxis, вы перечитываете и сканируете файл.Также, вызывая 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, будут занимать много времени над 8-гигабитным 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());
}
4 голосов
/ 06 августа 2010

Вам понадобится SAXReader для обработки больших файлов XML.

2 голосов
/ 06 августа 2010

Если вы используете MS SQL Server, используйте XML Bulk Load именно для этого. Статья базы знаний

2 голосов
/ 06 августа 2010

Вы изучали использование SAX-парсера?Microsoft не распространяет ни одного, но в Интернете есть несколько примеров.С парсером SAX вы, по сути, читаете файл как поток и запускаются события, которые вы можете прослушивать, а не загружать все это в DOM в памяти, чего вы явно не можете сделать.Я не слишком разбираюсь в использовании SAX-парсеров, поэтому у меня нет конкретных рекомендаций, но многие Java-разработчики делали XML таким образом годами.

0 голосов
/ 06 августа 2010

Взгляните на этот проект, который разбивает XML-файлы на более мелкие, чтобы решить вашу проблему:

Разделение больших файлов XML на маленькие файлы: http://www.codeproject.com/KB/XML/SplitLargeXMLintoSmallFil.aspx

...