У меня есть 16 больших XML-файлов. Когда я говорю Большой, говорю я в гигабайтах. Один из этих файлов составляет более чем 8 ГБ. Несколько из них - более чем 1 ГБ. Они даны мне от внешнего поставщика.
Я пытаюсь импортировать XML в базу данных так, чтобы я мог уничтожить его в таблицы. В настоящее время я передаю 10 000 записей потоком за один раз из файла в память и вставляю блоб. Я использую SSIS с задачей сценария сделать это. Это на самом деле ОЧЕНЬ быстро для всех файлов, кроме файла на 8 ГБ.
Я не могу загрузить весь файл в xml документ. Я не могу подчеркнуть это достаточно. Это было повторением 1, и файлы так огромны, что система просто запирает попытку иметь дело с этими файлами, 8 ГБ один в частности.
Я выполнил свой текущий "разделитель файла", и он провел 7 часов на импорт данных XML и все еще не был сделан. Это импортировало 363 блока 10 000 записей из файла на 8 ГБ и все еще не было сделано.
К вашему сведению вот то, как я в настоящее время передаю свои файлы потоком в память (10 000 записей за один раз). Я нашел код по http://blogs.msdn.com/b/xmlteam/archive/2007/03/24/streaming-with-linq-to-xml-part-2.aspx
private static IEnumerable 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.
Править:
Вот код что на самом деле страницы мои данные с помощью методологии потоковой передачи.
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);
}
}
Похоже, что вы перечитываете XML-файл снова и снова на каждом шаге, каждый раз, когда вы используете бит from p в 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 займут много времени на xml файле размером 8gb.
Update: Я думаю, что вышеприведенный вариант все равно будет итерировать файл больше раз, чем мы хотим, вам нужно что-то более предсказуемое, например:
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());
}
Если вы используете MS SQL Server, используйте массовую загрузку XML именно для этого.
Статья базы знаний
Вам понадобится SAXReader для работы с большими XML файлами.
Рассматривали ли вы возможность использования парсера SAX? Он не распространяется компанией Microsoft, но в Интернете есть несколько примеров. При использовании SAX парсера, вы читаете файл как поток и происходят события, которые вы можете отслеживать, а не загружать все в DOM в памяти, что вы, очевидно, не сможете сделать. Я не слишком много знаю об использовании SAX парсеров, поэтому у меня нет конкретной рекомендации, но многие Java-специалисты работают с XML таким образом уже много лет.
Взгляните на этот проект, который разбивает файлы XML на более мелкие, чтобы решить вашу проблему:
Разделите большие файлы XML на маленькие файлы: http: //www.codeproject. com / KB / XML / SplitLargeXMLintoSmallFil.aspx