2009-05-12 320 views
7

Necesito cargar un archivo CSV masivo (16GB, 65+ millones de registros) a una sola tabla en una base de datos de SQL Server 2005. ¿Alguien tiene alguna sugerencia sobre la mejor manera de hacer esto?Cargar un archivo CSV masivo a la base de datos de SQL Server

detalles

Actualmente estoy usando una aplicación de consola de C# (.NET framework 2.0) para dividir el archivo de importación en archivos de 50000 registros, a continuación, procesar cada archivo. Cargo los registros en la base de datos desde la aplicación de consola usando la clase SqlBulkCopy en lotes de 5000. Para dividir los archivos lleva aproximadamente 30 minutos, y para cargar todo el conjunto de datos (más de 65 millones de registros) toma aproximadamente 4.5 horas. El tamaño del archivo generado y el tamaño de carga por lotes son ambos ajustes de configuración, y estoy investigando para aumentar el valor de ambos para mejorar el rendimiento. Para ejecutar la aplicación, utilizamos un servidor de cuatro núcleos con 16 GB de RAM. Este servidor es también el servidor de la base de datos.

actualización

Dadas las respuestas hasta el momento, tenga en cuenta que antes de la importación:

  • La tabla de base de datos está truncada, y todos los índices y las restricciones se eliminan.
  • La base de datos se ha reducido y se ha reclamado espacio en el disco.

Después de la importación se ha completado:

  • Los índices se vuelven a crear

Si puede sugerir enfoques diferentes, o cómo podemos mejorar la aplicación de importación existente, lo agradecería . Gracias.

pregunta relacionada

La siguiente pregunta puede ser de utilidad para otras personas que se ocupan de este problema:

Solución

He investigado la afecta la alteración del tamaño del lote, y el tamaño de los archivos divididos, y encontró que los lotes de 500 registros y los archivos divididos de 200,000 registros funcionan mejor para mi aplicación. El uso del SqlBulkCopyOptions.TableLock también ayudó. Consulte la respuesta a este question para obtener más detalles.

También analicé el uso de un paquete SSIS DTS y un script SQL BULK INSERT. El paquete SSIS apareció más rápido, pero no me ofreció la posibilidad de registrar registros inválidos, etc. El script SQL BULK INSERT, aunque era más lento que el paquete SSIS, era considerablemente más rápido que la aplicación C#. Me permitió registrar errores, etc., y por esta razón, estoy aceptando la respuesta BULK INSERT de ConcernedOfTunbridgeWells como la solución. Soy consciente de que esta puede no ser la mejor respuesta para todos los que enfrentan este problema, pero responde a mi problema inmediato.

Gracias a todos los que respondieron.

Saludos, MagicAndi

Respuesta

5

BULK INSERT se ejecuta desde el mismo DBMS, leyendo los archivos descritos por un archivo de control bcp desde un directorio en el servidor (o montado en él). Escriba una aplicación que divide el archivo en fragmentos más pequeños, los coloca en un directorio apropiado y ejecuta un contenedor que ejecuta una serie de BULK INSERTS. Puede ejecutar varios hilos en paralelo si es necesario.

Esto es probablemente tan rápido como se obtiene una carga masiva. Además, si hay una clave de particionamiento adecuada disponible en el archivo de carga masiva, coloque la tabla de etapas en un esquema de partición.

Además, si está cargando a granel en una tabla con un índice agrupado, asegúrese de que los datos estén ordenados en el mismo orden que el índice. Merge sort es tu amigo para grandes conjuntos de datos.

+0

Eso es lo que hace la clase SqlBulkCopy. –

+0

No es estrictamente cierto. SqlBulkCopy envuelve la API de carga masiva OLEDB que todavía está empujando los datos a través del enlace cliente-servidor. BULK INSERT se ejecuta en proceso en el servidor. – ConcernedOfTunbridgeWells

+0

ConcernedOfTurnbridgeWells, gracias por la respuesta +1. Pruebo su solución y dejo algunos comentarios lo antes posible. – MagicAndi

2

La clase SqlBulkCopy que ya está utilizando va a ser su mejor apuesta. Lo mejor que puede hacer desde aquí en su código C# es experimentar con su sistema y datos particulares para ver qué tamaños de lote funcionan mejor. Pero ya estás haciendo eso.

Más allá del código de cliente, puede haber algunas cosas que puede hacer con el servidor para hacer la carrera de importación de manera más eficiente:

  • intente ajustar el tamaño de la tabla y la base de datos antes de iniciar la importación a algo grande lo suficiente para mantener todo el conjunto. No desea confiar en crecer automáticamente en el medio de esto.

  • Dependiendo de cómo se ordenan los datos y los índices en la tabla, puede hacer un poco mejor para soltar los índices que no coinciden con el orden en que se importan los registros, y luego volver a crearlos después del importar.

  • Finalmente, es tentador intentar ejecutar esto en paralelo, con algunos subprocesos haciendo inserciones masivas a la vez. Sin embargo, el cuello de botella más grande es casi seguro el rendimiento del disco. Cualquier cosa que pueda hacer para mejorar el servidor físico (discos nuevos, san, etc.) ayudará mucho más.

+0

Joel, gracias por su respuesta. Consulte la pregunta actualizada para obtener nueva información, respondiendo a algunos de los puntos de su respuesta. Sin embargo, me sorprendió un poco ver que recomendaba el uso de hilos pararllel. Los subprocesos se subirán a la misma tabla de base de datos en el servidor. ¿La operación de carga masiva de un hilo no bloquea la tabla y conduce a los otros subprocesos que esperan que libere la tabla? – MagicAndi

+0

No era mi intención recomendar hilos paralelos. Intenté decir que sería tentador, pero no es probable que te lleve a ninguna parte porque el rendimiento del disco es más importante. –

+0

Gracias por aclarar. +1. – MagicAndi

0

Últimamente, tuve que cargar/importar muchas cosas también (creó un script PHP).

Decidí procesarlos record-for-record.

Por supuesto, se necesita más tiempo, pero para mí, los siguientes puntos eran importantes: - pausa fácilmente el proceso - una mejor depuración

Esto es sólo un consejo.

cordiales, Benedikt

+0

Benedikt, usando la aplicación C#, todavía estoy procesando cada registro mientras leo los archivos generados. Esto me permite validar cada registro, etc. antes de intentar cargar, si así lo quisiera. – MagicAndi

3

¿Usted ha intentado SSIS (SQL Server Integration Services).

+0

Chris, gracias por su respuesta, me interesaría seguir utilizando el SSIS. ¿Podría vincular a ejemplos de uso de SSIS para cargar datos de un archivo? Gracias. – MagicAndi

+0

SSIS tiene un lector de CSS nativo (fuente de archivo plano). Dirija el lector hacia el tipo de salida correcto también. También se supone que SSIS agrupa los insertos. Desafortunadamente, la enseñanza de SSIS es un tema más amplio de lo que permite este formato, y SSIS es de naturaleza bastante gráfica. –

0

BULK INSERT probablemente ya sea la forma más rápida. Puede obtener un rendimiento adicional eliminando índices y restricciones al insertarlos y restablecerlos más tarde. El mayor impacto en el rendimiento proviene de los índices agrupados.

0

¿Has probado SQL Server Integration Services para esto? Puede manejar mejor un archivo de texto tan grande

0

Solo para verificar, su inserción será más rápida si no hay índices en la tabla en la que está insertando.

+0

Esto es parcialmente cierto. He visto importaciones por lotes tomar _longer_ al intentar esto, porque los datos de importación ya coinciden con la orden de índice. –

2

Usted puede ser capaz de salvar el paso de la división de los archivos de la siguiente manera:

  • una instancia de un IDataReader para leer los valores del archivo CSV de entrada. Hay varias formas de hacerlo: probablemente lo más fácil sea utilizar el controlador Microsoft OleDb Jet. Busque esto en Google si necesita más información, por ej. hay alguna información en this StackOverflow question.

    Un método alternativo es utilizar una técnica como la utilizada por www.csvreader.com.

  • Cree una instancia de un objeto SqlBulkCopy, establezca las propiedades BatchSize y BulkCopyTimeout en los valores apropiados.

  • Pase el IDataReader al método SqlBulkCopy.WriteToServer.

He utilizado esta técnica con éxito con archivos grandes, pero no tan grandes como los suyos.

1

Consulte this y this publicaciones en el blog para comparar. Parece que la mejor alternativa es usar BulkInsert con la opción TABLOCK establecida en verdadero.

+0

Santiiiii, gracias por los enlaces, muy apreciados. +1 – MagicAndi

0

Mi escenario para cosas como que es: Crear paquetes SSIS en el servidor SQL, que el uso de Bluk insertar en SQL, Crear procedimiento almacenado dentro de la base de datos para puede ejecutar ese paquete de código T-SQL

Después de que el envío archivo para insertar bluk al servidor SQL utilizando FTP y llamar al paquete SSIS usinfg procedimiento almacenado

Cuestiones relacionadas