¿Cuál es más rápido: múltiples INSERTs individuales o un INSERT de varias filas?


184

Estoy tratando de optimizar una parte de mi código que inserta datos en MySQL. ¿Debo encadenar INSERT para hacer un enorme INSERT de varias filas o son varios INSERT separados más rápido?

Respuestas:


287

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

El tiempo requerido para insertar una fila está determinado por los siguientes factores, donde los números indican proporciones aproximadas:

  • Conexión: (3)
  • Enviando consulta al servidor: (2)
  • Consulta de análisis: (2)
  • Insertar fila: (1 × tamaño de fila)
  • Insertar índices: (1 × número de índices)
  • Clausura: (1)

A partir de esto, debería ser obvio que enviar una declaración grande le ahorrará una sobrecarga de 7 por instrucción de inserción, que en una lectura posterior del texto también dice:

Si está insertando muchas filas desde el mismo cliente al mismo tiempo, use instrucciones INSERT con múltiples listas de VALORES para insertar varias filas a la vez. Esto es considerablemente más rápido (muchas veces más rápido en algunos casos) que el uso de instrucciones INSERT de una sola fila por separado.


27
¿Cómo se aplica esta respuesta si múltiples INSERTs individuales están dentro de la misma transacción de base de datos?
Pinch

2
Cuántas filas puedo insertar a la vez usando una sola instrucción de inserción. ¿me permite insertar 10000 filas a la vez?
Naresh Ramoliya

10
@Pinch El uso de una transacción mientras se realizan ~ 1.5k upserts (inserción / actualizaciones) disminuyó el tiempo que la operación tomó de ~ 1.5 segundos a ~ 0.2 segundos. O, en otras palabras, lo hizo un 86% más rápido en comparación con las inserciones de una sola fila. Maldición.
fgblomqvist

1
Nota: Parece ser muy diferente en MSSQL: stackoverflow.com/questions/8635818/…
marsze

¿Qué tal usar la Declaración Preparada para insertar múltiples inserciones repetitivas?
priyabagus

151

Sé que estoy respondiendo esta pregunta casi dos años y medio después de que se hizo, pero solo quería proporcionar algunos datos concretos de un proyecto en el que estoy trabajando en este momento que muestra que, de hecho, hacer varios bloques de VALOR por inserción es MUCHO más rápido que las instrucciones INSERT de bloque VALOR único secuencial

El código que escribí para este punto de referencia en C # usa ODBC para leer datos en la memoria de una fuente de datos MSSQL (~ 19,000 filas, todas se leen antes de que comience la escritura), y el conector MySql .NET (Mysql.Data. *) INSERTE los datos de la memoria en una tabla en un servidor MySQL a través de declaraciones preparadas. Fue escrito de tal manera que me permite ajustar dinámicamente el número de bloques de VALOR por INSERTAR preparado (es decir, insertar n filas a la vez, donde podría ajustar el valor de n antes de una ejecución). También realicé la prueba varias veces para cada n.

Hacer bloques de VALOR individuales (por ejemplo, 1 fila a la vez) tardó 5,7 - 5,9 segundos en ejecutarse. Los otros valores son los siguientes:

2 filas a la vez: 3.5 - 3.5 segundos
5 filas a la vez: 2.2 - 2.2 segundos
10 filas a la vez: 1.7 - 1.7 segundos
50 filas a la vez: 1.17 - 1.18 segundos
100 filas a la vez: 1.1 - 1.4 segundos
500 filas a la vez: 1.1 - 1.2 segundos
1000 filas a la vez: 1.17 - 1.17 segundos

Entonces, sí, incluso agrupar 2 o 3 escrituras juntas proporciona una mejora dramática en la velocidad (tiempo de ejecución cortado por un factor de n), hasta llegar a algún lugar entre n = 5 y n = 10, momento en el cual la mejora disminuye notablemente, y en algún lugar en el rango n = 10 a n = 50 la mejora se vuelve insignificante.

Espero que ayude a las personas a decidir sobre (a) si usar la idea de reparación múltiple y (b) cuántos bloques de VALOR crear por declaración (suponiendo que desee trabajar con datos que pueden ser lo suficientemente grandes como para llevar la consulta más allá del tamaño máximo de consulta) para MySQL, que creo que es de 16 MB por defecto en muchos lugares, posiblemente más grande o más pequeño dependiendo del valor de max_allowed_packet establecido en el servidor).


1
Solicitud de aclaración: es su tiempo "segundos por fila" o "segundos totales".
EngrStudent

3
Segundos en total, por lo que los segundos por fila son los divididos por las ~ 19,000 filas. Aunque es un número pequeño, entonces quizás las filas / segundo sean una mejor métrica si está buscando un número fácilmente comparable.
Jon Kloske

Por cierto, hay un ejemplo de código .NET para el enfoque que describo anteriormente en esta respuesta mía relacionada: stackoverflow.com/questions/25377357/…
Jon Kloske

18

Un factor importante será si está utilizando un motor transaccional y si tiene la confirmación automática activada.

La confirmación automática está activada de manera predeterminada y probablemente desee dejarla activada; por lo tanto, cada inserción que realice hace su propia transacción. Esto significa que si realiza una inserción por fila, va a confirmar una transacción para cada fila.

Suponiendo un solo subproceso, eso significa que el servidor necesita sincronizar algunos datos en el disco por CADA FILA. Debe esperar a que los datos lleguen a una ubicación de almacenamiento persistente (es de esperar que la memoria RAM respaldada por batería en su controlador RAID). Esto es inherentemente lento y probablemente se convertirá en el factor limitante en estos casos.

Por supuesto, supongo que está utilizando un motor transaccional (generalmente innodb) Y que no ha modificado la configuración para reducir la durabilidad.

También supongo que está utilizando un solo hilo para hacer estas inserciones. El uso de varios subprocesos enturbia un poco las cosas porque algunas versiones de MySQL tienen un compromiso de trabajo grupal en innodb; esto significa que varios subprocesos que realizan sus propios compromisos pueden compartir una sola escritura en el registro de transacciones, lo cual es bueno porque significa menos sincronizaciones con el almacenamiento persistente .

Por otro lado, el resultado es que REALMENTE DESEA USAR insertos de varias filas.

Hay un límite sobre el cual se vuelve contraproducente, pero en la mayoría de los casos es de al menos 10,000 filas. Entonces, si las agrupa hasta 1,000 filas, probablemente esté seguro.

Si está utilizando MyISAM, hay muchas otras cosas, pero no lo aburriré con eso. Paz.


1
¿Hay alguna razón por la que se vuelve contraproducente después de un punto? También lo he visto antes, pero no estaba seguro de por qué.
Dhruv Gairola

1
¿Sabe si hay algún punto en el procesamiento por lotes de inserciones de MySQL al usar transacciones ? Me pregunto si puedo ahorrarme la molestia de tener que generar el comando SQL de valores múltiples si mi biblioteca subyacente (Java JDBC - mysql-connector-java-5.1.30) no se compromete realmente hasta que se lo indique.
RTF

@RTF Creo que necesitará realizar una pequeña prueba para determinar ese comportamiento en su situación, ya que es un comportamiento altamente específico de implementación, pero en muchos casos las transacciones sí deberían proporcionar ganancias de rendimiento similares.
Jasmine Hegman

9

Envíe tantas inserciones a través del cable a la vez como sea posible. La velocidad de inserción real debe ser la misma, pero verá ganancias de rendimiento por la reducción de la sobrecarga de la red.


7

En general, cuanto menor sea el número de llamadas a la base de datos, mejor (es decir, más rápido, más eficiente), así que intente codificar las inserciones de tal manera que minimice los accesos a la base de datos. Recuerde, a menos que esté utilizando un grupo de conexiones, cada acceso a la base de datos debe crear una conexión, ejecutar el sql y luego cortar la conexión. ¡Un poco de gastos generales!


¿Qué pasa si se usa la conexión persistente?
dusoft el

66
Todavía hay gastos generales. El tiempo de tránsito solo (hacia y desde cada inserto por separado) será rápidamente perceptible si está haciendo miles de insertos.
RC.

4

Tu podrías querer :

  • Verifique que la confirmación automática esté desactivada
  • Conexión abierta
  • Envíe varios lotes de inserciones en una sola transacción (¿tamaño de aproximadamente 4000-10000 filas?)
  • Conexión cercana

Dependiendo de qué tan bien escale su servidor (definitivamente está bien con PostgreSQl, Oracley MSSQL), haga lo anterior con múltiples hilos y múltiples conexiones.


3

En general, las inserciones múltiples serán más lentas debido a la sobrecarga de la conexión. Hacer múltiples inserciones a la vez reducirá el costo de sobrecarga por inserción.

Dependiendo del idioma que esté utilizando, posiblemente pueda crear un lote en su lenguaje de programación / scripting antes de ir a la base de datos y agregar cada inserción al lote. Entonces podrá ejecutar un lote grande utilizando una operación de conexión. Aquí hay un ejemplo en Java.


3

MYSQL 5.5 Una instrucción de inserción sql tomó ~ 300 a ~ 450ms. mientras que las estadísticas a continuación son para declaraciones de inserción múltiple en línea.

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

Yo diría que en línea es el camino a seguir :)


0

Es ridículo lo mal que Mysql y MariaDB están optimizados cuando se trata de inserciones. Probé mysql 5.7 y mariadb 10.3, no hay diferencia real en esos.

He probado esto en un servidor con discos NVME, 70,000 IOPS, 1,1 GB / seg de rendimiento seq y eso es posible full duplex (lectura y escritura).
El servidor también es un servidor de alto rendimiento.
Le dio 20 GB de ram.
La base de datos está completamente vacía.

La velocidad que recibo fue de 5000 inserciones por segundo al hacer inserciones de varias filas (lo probé con fragmentos de datos de 1 MB hasta 10 MB)

Ahora la pista:
si agrego otro hilo e inserto en las MISMAS tablas, de repente tengo 2x5000 / seg. Un hilo más y tengo 15000 totales / seg.

Considere esto: cuando se inserta ONE thread, significa que puede escribir secuencialmente en el disco (con excepción de los índices). Cuando se usan subprocesos, en realidad degrada el rendimiento posible porque ahora necesita hacer muchos más accesos aleatorios. Pero la comprobación de la realidad muestra que mysql está tan mal optimizado que los hilos ayudan mucho.

El rendimiento real posible con dicho servidor es probablemente de millones por segundo, la CPU está inactiva y el disco está inactivo.
La razón es claramente que mariadb al igual que mysql tiene retrasos internos.


@Craftables necesita desarrollo externo, no se puede hacer dentro de mysql. Subprocesos significa que utiliza múltiples conexiones al servidor, divide la consulta en varios fragmentos (por ejemplo, dividiéndolo en partes pares por clave principal). Logré obtener hasta 10,000 veces el rendimiento usando este método en tablas muy grandes. Las consultas que se ejecutarían durante 40,000 segundos pueden finalizar en 2-3 minutos SI usa múltiples hilos y su mysql está altamente optimizado.
John

@John Interesante y puede tener algunas aplicaciones realmente buenas ... pero ... Si divide la consulta en varios fragmentos, ¿cómo maneja las transacciones? Y también considere el siguiente escenario: la Tabla x tiene una columna 'parent_id' que se relaciona con la misma tabla 'id'. En algún lugar dentro de sus datos tiene INSERT INTO x ( id, parent_id) VALUES (1, NULL). Uno de los siguientes conjuntos de valores enlaza con esa fila. Si se divide en fragmentos y ese conjunto llega a otro fragmento, puede procesarse antes del primero, fallando todo el proceso. ¿Alguna idea de cómo lidiar con eso?
zozo

@zozo esto es útil para inserciones masivas y consultas masivas. Las transacciones arruinarían el rendimiento de todos modos, ya que incluyen una gran cantidad de almacenamiento en búfer de datos. Pero también puede usar transacciones en inserciones o consultas de subprocesos múltiples.
John

-2

las inserciones múltiples son más rápidas, pero se debe haber. otro truco es deshabilitar las restricciones, los controles temporales hacen que las inserciones sean mucho más rápidas. No importa que tu mesa lo tenga o no. Por ejemplo, pruebe deshabilitar claves externas y disfrute de la velocidad:

SET FOREIGN_KEY_CHECKS=0;

por supuesto, debe volver a encenderlo después de las inserciones:

SET FOREIGN_KEY_CHECKS=1;

Esta es una forma común de insertar grandes datos. la integridad de los datos puede romperse, por lo que debe preocuparse por eso antes de deshabilitar las comprobaciones de claves externas.


1
No tengo idea de por qué la gente votó por esto por dos razones: 1. No tiene nada que ver con la pregunta 2. Es una idea realmente mala (con algunas excepciones, como el dumping o los cambios de temperatura estructural, pero mala en general). Las verificaciones están ahí por una razón: están ahí para garantizar la consistencia de los datos. Disminuyen la velocidad porque se aseguran de que no inserte o cambie los datos que no debería. Intente optimizar las consultas de la manera correcta; en cualquier entorno empresarial crítico, esto significaría la muerte de la aplicación, ya que independientemente de cuán cuidadoso sea, las cosas fallarán en algún momento.
zozo

1
tal vez, pero esta opción es extremadamente efectiva en la importación de tablas grandes y muy práctica y podría darles a algunas personas una idea de cómo pueden hacer que la inserción de datos sea mucho más rápida.
MSS
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.