2010-08-06 26 views
6

Tengo 16 archivos xml grandes. Cuando digo Large, estoy hablando en gigabytes. Uno de estos archivos tiene más de 8 GB. Varios de ellos tienen más de 1 gb. Estos me son proporcionados por un proveedor externo.Problemas con los GRANDES archivos XML

Estoy tratando de importar el XML en una base de datos para poder triturarlo en tablas. Actualmente, transfiero 10.000 registros a la vez fuera del archivo a la memoria e inserto el blob. Yo uso SSIS con una tarea de script para hacer esto. Esto es realmente MUY rápido para todos los archivos, excepto el archivo de 8 GB.

No puedo cargar todo el archivo en un documento xml. No puedo enfatizar esto lo suficiente. Esa fue la iteración 1 y los archivos son tan grandes que el sistema simplemente se bloquea intentando tratar con estos archivos, el de 8 gb en particular.

Corrí mi actual "divisor de archivos" y pasó 7 horas en la importación de los datos xml y todavía no estaba hecho. Importó 363 bloques de 10.000 registros del archivo de 8 GB y todavía no estaba hecho.

FYI, así es como actualmente estoy transmitiendo mis archivos en la memoria (10,000 registros a la vez). He encontrado el código en 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(); 
      } 
     } 

Por lo tanto, funciona bien en todos los archivos, excepto los 8 GB aquel en el que se tiene que transmitir más y más en el archivo que se necesita más tiempo y más tiempo.

Lo que me gustaría hacer es dividir el archivo en trozos más pequeños, pero el separador debe ser rápido. Luego, el transmisor y el resto del proceso pueden ejecutarse más rápidamente. ¿Cuál es la mejor forma de dividir los archivos? Idealmente, me dividiría en código en SSIS.

EDIT:

Aquí está el código que realmente páginas fuera mis datos utilizando la metodología de streaming.

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); 
        } 
       } 
+0

Los documentos XmlReader dicen que para los archivos grandes que "requieren una cantidad sustancial de tiempo para su procesamiento", debe "crear una implementación IStream personalizada". ¿Has probado eso? – Ken

+0

"Actualmente, reproduzco 10.000 registros a la vez del archivo en la memoria e inserto el blob" - ¿de verdad está usando un BLOB? No entiendo exactamente lo que intenta hacer aquí porque MS SQL Server puede trabajar de forma nativa con XML. – Hut8

+0

Elimina las llamadas '.Close()'. La declaración 'using' se encarga de eso en estas dos instancias particulares. –

Respuesta

5

Parece que usted está relectura en el archivo XML una y otra vez cada paso, cada vez que se utiliza el bit from p in SimpleStreamAxis que son re-lectura y la exploración en el archivo. Además, al llamar a Count() está caminando la página completa cada vez.

intentar algo como esto:

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); 
} 

Nota esto no se ha probado, pero se debe de esperar conseguir la idea. Debe evitar enumerar a través del archivo más de una vez, operaciones como Count() y Take/Skip van a tomar mucho tiempo en un archivo de 8gb xml.

actualización: Creo que lo anterior todavía habrá una iteración en el archivo más veces de lo que quieren, se necesita algo un poco más predecible como esto:

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()); 
} 
0

Echa un vistazo a este proyecto que divide archivos XML en otros más pequeños para conquistar su problema:

grandes archivos XML divide en pequeños archivos: http://www.codeproject.com/KB/XML/SplitLargeXMLintoSmallFil.aspx

+0

Ya lo he visto y lo descarté. El código fuente no se incluye en la descarga. El código que se muestra no está completo (el método se desactiva para codificar). Tampoco estoy seguro de la ventaja de la velocidad de ese método frente a cómo ya estoy dividiendo el xml. – Josh

4

Vas a quiere un SAXReader para manejar archivos XML grandes.

2

¿Has estudiado el uso de un analizador SAX? No hay uno distribuido por Microsoft, pero hay algunos ejemplos en la web. Con un analizador SAX, esencialmente lees el archivo como un flujo y el fuego de eventos que puedes escuchar frente a cargar todo en un DOM en memoria que obviamente no puedes hacer. No sé demasiado sobre el uso de analizadores SAX, por lo que no tengo una recomendación específica, pero mucha gente de Java ha hecho XML de esta manera durante años.

+5

XmlReader, como se usa en el código de pregunta, es similar a un analizador SAX, solo que es pull en lugar de push. No haría ninguna diferencia a este problema en particular. –

Cuestiones relacionadas