2009-06-30 14 views
6

Necesito insertar aproximadamente 1,8 millones de filas de un archivo CSV en una base de datos MySQL. (solo una tabla)MySQL Inserción de grandes conjuntos de datos del archivo con Java

Actualmente utiliza Java para analizar el archivo e insertar cada línea.

Como se puede imaginar, esto toma bastantes horas para ejecutarse. (10)

La razón por la que no estoy conectando directamente desde el archivo al archivo base, es que los datos deben manipularse antes de agregarlos a la base de datos.

Este proceso debe ser ejecutado por un administrador de TI allí. Así que lo he configurado como un buen archivo por lotes para que se ejecuten después de que suelten el nuevo archivo csv en la ubicación correcta. Por lo tanto, tengo que hacer que esto funcione bien al dropear el archivo en una ubicación determinada y ejecutar un archivo por lotes. (Entorno de Windows)

Mi pregunta es, ¿cuál sería la forma más rápida de insertar esta cantidad de datos; inserciones grandes, a partir de un archivo de análisis temp o de una inserción a la vez? alguna otra idea posiblemente?

La segunda pregunta es, ¿cómo puedo optimizar mi instalación de MySQL para permitir inserciones muy rápidas? (habrá un punto en el que también se requiere una gran selección de todos los datos)

Nota: la tabla eventualmente se perderá y todo el proceso se ejecutará nuevamente en una fecha posterior.

Algunas aclaraciones: actualmente utilizando ... opencsv.CSVReader para analizar el archivo y luego hacer una inserción en cada línea. Sin embargo, estoy relatando algunas columnas e ignorando otras.

Más aclaración: local DB tabla MyISAM

+3

No inserte por línea, por lotes hasta un montón de filas y crea menos DB llama, su velocidad subirá drásticamente. Vea mi respuesta para un simple ejemplo de procesamiento por lotes de PreparedStatement. – Hardwareguy

Respuesta

14

Consejos para la rápida inserción:

  • LOAD DATA INFILE utilizar la sintaxis para que MySQL analizarlo e insertarlo, incluso si tiene que destrozar y alimentarlo después de la manipulación.
  • Utilice esta sintaxis de inserción:

    inserción en la tabla (col1, col2) valores (val1, val2), (val3, Val4), ...

  • Retire todas las llaves/índices antes de la inserción .

  • Hazlo en la máquina más rápida que tengas (principalmente en IO, pero la RAM y la CPU también importan).Tanto el servidor de base de datos como el cliente de inserción recuerdan que pagará dos veces el precio de IO (una vez que lee, la segunda inserción)
+0

Tener el archivo en el servidor es, con mucho, el más rápido, pero si no tiene ese tipo de acceso, aún puede usar LOAD DATA LOCAL INFILE. Solo asegúrate de usar una conexión comprimida si es un archivo grande. –

+1

gracias, de hecho, al final cargué los datos en directo, ya que estaba utilizando cargar el archivo local de datos. Luego escribí una serie de consultas sql algo complejas para crear otra tabla temporal en el formato que quería. El tiempo total ahora es de 30 segundos para 1,8 millones de registros. No está mal desde el orignal 10 horas que el desarrollador original creó. Todo esto hecho en mysql, no se requiere java en absoluto. –

+3

¡Impresionante! La opción 2 (insertar sintaxis) me bajó de 77 minutos a 26 segundos al insertar 400,000 filas. –

1

realmente se debe utilizar carga de datos en la propia consola de MySQL para esto y no funciona a través del código ...

LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table; 

Si necesita manipular los datos, aún recomendaría manipular en la memoria, reescribir en un archivo plano, y empujarlo a la base de datos usando LOAD DATA, creo que debería ser más eficiente.

+0

-1 dijo que necesitaba manipular los datos antes de ponerlos en el DB – Hardwareguy

+0

@Hardwareguy: vea el cambio que agregué (antes de ver su comentario :) –

+0

Eliminaré mi menos uno pero aún no lo creo esa es la mejor manera. – Hardwareguy

0

¿No sería más rápido si usó LOAD DATA INFILE en lugar de insertar cada fila?

+0

-1. Cita de la pregunta: "La razón por la que no estoy conectando directamente desde el archivo a la base de datos, es que los datos deben ser manipulados antes de agregarlos a la base de datos" – PatrikAkerstrand

+0

Vi esto: manipular sus datos, guardarlos en una archivo temporal, llame a "cargar archivo de datos", elimine el archivo temporal. – Pierre

4

Probablemente elegiría un número grande, como 10k filas, y cargaré que muchas filas del CSV, masajes de datos, y hacer una actualización por lotes, luego repita hasta que haya pasado por la csv completa. Dependiendo del masaje/cantidad de datos, las filas de 1,8 milipulgadas no deberían tomar 10 horas, más o menos 1-2 horas dependiendo de su hardware.

edit: whoops, omitió una parte bastante importante, su estafa tiene que tener autocommit establecido en falso, el código que he copiado lo hacía como parte del método GetConnection().

Connection con = GetConnection(); 
con.setAutoCommit(false); 
      try{ 
       PreparedStatement ps = con.prepareStatement("INSERT INTO table(col1, col2) VALUES(?, ?)"); 
       try{ 
        for(Data d : massagedData){ 
         ps.setString(1, d.whatever()); 
             ps.setString(2, d.whatever2()); 
              ps.addBatch(); 
        } 
        ps.executeBatch(); 
       }finally{ 
        ps.close(); 
       } 
      }finally{ 
       con.close(); 
      } 
+1

1-2 horas todavía es lento como el infierno.LOAD FROM INFILE finaliza en cuestión de segundos si está en el mismo formato que la tabla, especialmente si el archivo .csv ya reside en el servidor. Pruébalo, es increíblemente rápido. Por lo general, no desea un conjunto de datos incompleto en su base de datos, por lo que tendrá que usar una transacción y bloquear tablas ... Personalmente, no conozco ningún servidor de producción donde bloquear tablas durante 1-2 horas sea aceptable. –

+0

Dijo filas de 1.8 mil. También esta es una tabla temporal por lo que no va a bloquear ninguna otra tabla. – Hardwareguy

1

Otra idea: ¿Utiliza un PreparedStatement para insertar sus datos con JDBC?

+0

Las declaraciones preparadas con addBatch son la forma en que siempre hago esto. – Hardwareguy

+0

interesante, ¿esto ofrece alguna mejora en el rendimiento? actualmente usando .... opencsv.CSVReader para analizar el archivo y luego hacer una inserción en cada línea. Sin embargo, estoy relatando algunas columnas e ignorando otras. –

+1

Hay un montón de sobrecarga en simplemente hacer una conexión a la base de datos. Verá una gran aceleración mediante inserciones de procesamiento por lotes. – Hardwareguy

0

correría tres hilos ...

1) lee el archivo de entrada y empuja cada fila en una cola de transformación 2) Pops de la cola, transforma los datos, y empuja en una cola db 3) Pops de la cola db e inserta los datos

de esta manera, puede estar leyendo los datos desde el disco mientras que los hilos db están esperando su IO para completar y viceversa

+1

Eso suena bien en teoría, pero el hilo 3 es donde va a suceder el 95% del trabajo, así que realmente no vas a ganar mucho paralelizando las tareas de hilo 1 y 2. – Hardwareguy

+0

Por supuesto, eso depende de las transformaciones. En mi experiencia, eso puede implicar muchas búsquedas de bases de datos para validar los campos. Si el archivo fuente está en un disco diferente al de los archivos de la base de datos, aún debería haber un aumento en el rendimiento. Si tienen que estar en el mismo disco, definitivamente agruparé las cosas en 1000 filas o más para reducir las búsquedas principales. – Reed

+0

Normalmente voy con 2 hilos (leer y analizar + cargar). No he trabajado en redes y servidores superiores (en este caso, el tercer hilo podría ser útil), pero normalmente las acciones de carga y base de datos tardan más que simplemente leer un archivo y analizar un par de enteros. – Pijusn

0

Si aún no se encuentra , intente utilizar el tipo de tabla MyISAM, solo asegúrese de leer sus deficiencias antes de Tú haces. En general, es más rápido que los otros tipos de tablas.

Si su tabla tiene índices, generalmente es más rápido soltarlos y luego volver a agregarlos después de la importación.

Si sus datos son todos de cadenas, pero es más adecuado como una base de datos relacional, será mejor que inserte enteros que indiquen otros valores en lugar de almacenar una cadena larga.

Pero, en general, sí, agregar datos a una base de datos lleva tiempo.

1

Dependiendo de qué es exactamente lo que hay que hacer con los datos antes de insertarlo sus mejores opciones en términos de velocidad son:

  • analizar el archivo en Java/hacer lo que tiene con los datos/escribir el "masajear" datos a un nuevo archivo CSV/use "load data infile" en eso.
  • Si su manipulación de datos es condicional (por ejemplo, necesita verificar la existencia de registros y hacer diferentes cosas según si es una inserción y una actualización, etc.), entonces (1) puede ser imposible. En ese caso, es mejor que haga inserciones/actualizaciones por lotes.
    Experimenta para encontrar el mejor tamaño de lote que funcione para ti (comenzando con 500-1000 debería estar bien). Dependiendo del motor de almacenamiento que esté utilizando para su tabla, es posible que deba dividir esto en varias transacciones, ya que tener una sola trama de 1.8M no hará maravillas en cuanto al rendimiento.
  • 1

    Probablemente su mayor problema de rendimiento no sea java sino mysql, en particular cualquier índice, restricción y clave externa que tenga en la tabla en la que está insertando. Antes de comenzar con sus inserciones, asegúrese de desactivarlos. Volver a habilitarlos al final requerirá una cantidad de tiempo considerable, pero es mucho más eficiente que hacer que la base de datos los evalúe después de cada declaración.

    También puede estar viendo problemas de rendimiento de mysql debido al tamaño de su transacción.Su registro de transacciones crecerá muy grande con tantas inserciones, por lo que realizar una confirmación después del número X de inserciones (digamos entre 10,000 y 100,000) también ayudará a insertar la velocidad.

    Desde la capa jdbc, asegúrese de estar utilizando los comandos addBatch() y executeBatch() en vez de su PreparedStatement en lugar de la ejecución normal executeUpdate().

    2

    ¿Está seguro de que ha deshabilitado las confirmaciones automáticas en el controlador JDBC?

    Este es el asesino de rendimiento típico para los clientes JDBC.

    +0

    No soy ... Lo veré ... gracias. –

    1

    Puede mejorar el rendimiento de inserción masiva de MySQL/Java utilizando la capacidad de procesamiento por lotes, en su conductor conector J JDBC.

    MySQL no maneja "adecuadamente" los lotes (ver el enlace de mi artículo, abajo), pero puede reescribir INSERT para hacer uso de la peculiar sintaxis de MySQL, p. se puede decir que el conductor vuelva a escribir dos insertos:

    INSERT INTO (val1, val2) VALUES ('val1', 'val2'); 
    INSERT INTO (val1, val2) VALUES ('val3', 'val4'); 
    

    como una sola instrucción:

    INSERT INTO (val1, val2) VALUES ('val1', 'val2'), ('val3','val4'); 
    

    (Tenga en cuenta que no estoy diciendo que necesidad de reescribir su SQL de esta manera, el driver lo hace cuando puede)

    Lo hicimos para una investigación de inserción masiva propia: hizo un orden de magnitud de diferencia. Usado con transacciones explícitas como lo mencionaron otros y verá una gran mejora en general.

    El valor de la propiedad controlador relevante es:

    jdbc:mysql:///<dbname>?rewriteBatchedStatements=true 
    

    Ver: A 10x Performance Increase for Batch INSERTs With MySQL Connector/J Is On The Way

    Cuestiones relacionadas