Estoy luchando con la importación masiva de una tabla InnoDB bastante grande que consta de aproximadamente 10 millones de filas (o 7 GB) (que para mí es la tabla más grande con la que he trabajado hasta ahora).
Investigué un poco sobre cómo mejorar la velocidad de importación de Inno y por el momento mi configuración se ve así:
/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8
import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;
Los datos se proporcionan en un CSV
archivo.
Actualmente pruebo mi configuración con 'volcados de prueba' más pequeños con 2 millones, 3 millones, ... filas cada uno y uso time import_script.sh
para comparar el rendimiento.
El inconveniente es que solo obtengo un tiempo de ejecución general, así que tengo que esperar a que termine la importación completa para obtener un resultado.
Mis resultados hasta ahora:
- 10 000 filas: <1 segundo
- 100 000 filas: 10 segundos
- 300 000 filas: 40 segundos
- 2 millones de filas: 18 minutos
- 3 millones de filas: 26 minutos
- 4 millones de filas: (cancelado después de 2 horas)
Parece que no hay una solución de 'libro de cocina' y uno tiene que descubrir la combinación óptima de configuraciones por su cuenta.
Además de las sugerencias sobre qué cambiar en mi configuración, también agradecería más información sobre cómo podría comparar mejor el proceso de importación / obtener más información sobre lo que está sucediendo y dónde podría estar el cuello de botella.
Traté de leer la documentación de la configuración que estoy cambiando, pero, una vez más, no estoy al tanto de ningún efecto secundario y si incluso podría disminuir el rendimiento con un valor mal elegido.
Por el momento, me gustaría probar una sugerencia de chat para usar MyISAM
durante la importación y cambiar el motor de la tabla después.
Me gustaría probar esto, pero por el momento mi DROP TABLE
consulta también tarda horas en terminar. (Lo que parece otro indicador de que mi configuración es menos que óptima).
Información adicional:
La máquina que estoy usando actualmente tiene 8 GB de RAM y un disco duro híbrido de estado sólido con 5400 RPM.
Si bien también apuntamos a eliminar datos obsoletos de la tabla en cuestión, todavía necesito una importación algo rápida para
a) probar automatic data cleanup feature
durante el desarrollo
yb) en caso de que nuestro servidor se bloquee, nos gustaría usar nuestro segundo servidor como reemplazo (que necesita datos actualizados, la última importación tardó más de 24 horas)
mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
Table: monster
Create Table: CREATE TABLE `monster` (
`monster_id` int(11) NOT NULL AUTO_INCREMENT,
`ext_monster_id` int(11) NOT NULL DEFAULT '0',
`some_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(250) NOT NULL,
`name` varchar(100) NOT NULL,
`address` varchar(100) NOT NULL,
`postcode` varchar(20) NOT NULL,
`city` varchar(100) NOT NULL,
`country` int(11) NOT NULL DEFAULT '0',
`address_hash` varchar(250) NOT NULL,
`lon` float(10,6) NOT NULL,
`lat` float(10,6) NOT NULL,
`ip_address` varchar(40) NOT NULL,
`cookie` int(11) NOT NULL DEFAULT '0',
`party_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '2',
`creation_date` datetime NOT NULL,
`someflag` tinyint(1) NOT NULL DEFAULT '0',
`someflag2` tinyint(4) NOT NULL,
`upload_id` int(11) NOT NULL DEFAULT '0',
`news1` tinyint(4) NOT NULL DEFAULT '0',
`news2` tinyint(4) NOT NULL,
`someother_id` int(11) NOT NULL DEFAULT '0',
`note` varchar(2500) NOT NULL,
`referer` text NOT NULL,
`subscription` int(11) DEFAULT '0',
`hash` varchar(32) DEFAULT NULL,
`thumbs1` int(11) NOT NULL DEFAULT '0',
`thumbs2` int(11) NOT NULL DEFAULT '0',
`thumbs3` int(11) NOT NULL DEFAULT '0',
`neighbours` tinyint(4) NOT NULL DEFAULT '0',
`relevance` int(11) NOT NULL,
PRIMARY KEY (`monster_id`),
KEY `party_id` (`party_id`),
KEY `creation_date` (`creation_date`),
KEY `email` (`email`(4)),
KEY `hash` (`hash`(8)),
KEY `address_hash` (`address_hash`(8)),
KEY `thumbs3` (`thumbs3`),
KEY `ext_monster_id` (`ext_monster_id`),
KEY `status` (`status`),
KEY `note` (`note`(4)),
KEY `postcode` (`postcode`),
KEY `some_id` (`some_id`),
KEY `cookie` (`cookie`),
KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8
fuente
SHOW CREATE TABLE yourtable\G
para mostrarnos la estructura de la tabla de esta tabla de 10 millones de filas.innodb_doublewrite = 0
), su instalación de MySQL no es segura: si tiene una falla de energía (no un bloqueo de MySQL), sus datos podrían corromperse silenciosamente.Respuestas:
Primero, necesita saber qué le está haciendo a InnoDB cuando coloca millones de filas en una tabla de InnoDB. Echemos un vistazo a la arquitectura InnoDB.
En la esquina superior izquierda, hay una ilustración del InnoDB Buffer Pool. Observe que hay una sección dedicada al búfer de inserción. Que hace eso Está diseñado para migrar los cambios a los índices secundarios del grupo de búferes al búfer de inserción dentro del espacio de tabla del sistema (también conocido como ibdata1). De forma predeterminada, innodb_change_buffer_max_size se establece en 25. Esto significa que se puede utilizar hasta el 25% del grupo de búferes para procesar índices secundarios.
En su caso, tiene 6.935 GB para el InnoDB Buffer Pool. Se utilizará un máximo de 1.734 GB para procesar sus índices secundarios.
Ahora mira tu mesa. Tienes 13 índices secundarios. Cada fila que procese debe generar una entrada de índice secundaria, acoplarla con la clave primaria de la fila y enviarla como un par desde Insert Buffer en el Buffer Pool al Insert Buffer en ibdata1. Eso sucede 13 veces con cada fila. Multiplique esto por 10 millones y casi puede sentir un cuello de botella.
No olvide que la importación de 10 millones de filas en una sola transacción agrupará todo en un segmento de reversión y llenará el espacio UNDO en ibdata1.
SUGERENCIAS
SUGERENCIA # 1
Mi primera sugerencia para importar esta tabla bastante grande sería
SUGERENCIA # 2
Deshágase de los índices duplicados. En tu caso, tienes
Ambos índices comienzan con
party_id
, puede aumentar el procesamiento del índice secundario en al menos un 7,6%, eliminando un índice de 13. Es necesario ejecutar eventualmenteSUGERENCIA # 3
Deshágase de los índices que no usa. Revise el código de su aplicación y vea si sus consultas usan todos los índices. Es posible que desee ver el uso de índice-pt para permitirle sugerir qué índices no se están utilizando.
SUGERENCIA # 4
Debe aumentar innodb_log_buffer_size a 64M ya que el valor predeterminado es 8M. Un búfer de registro más grande puede aumentar el rendimiento de E / S de escritura de InnoDB.
EPÍLOGO
Poniendo las dos primeras sugerencias en su lugar, haga lo siguiente:
party_id
índice.Quizás lo siguiente pueda ayudar
Importar los datos a
monster
. Entonces, ejecuta estoDARLE UNA OPORTUNIDAD !!!
ALTERNATIVA
Puede crear una tabla llamada
monster_csv
como tabla MyISAM sin índices y hacer esto:Importa tus datos a
monster_csv
. Luego, use mysqldump para crear otra importaciónEl archivo mysqldump
data.sql
extenderá los comandos INSERT para importar 10,000-20,000 filas a la vez.Ahora, solo carga el mysqldump
Finalmente, deshazte de la tabla MyISAM
fuente
monster
tabla) en menos de 20 minutos cuando no tenía claves en las tablas de InnoDB. Agregar llaves tomó aprox. otros 20 min. Yo diría que esto resuelve mi problema en este caso. ¡Muchas gracias!Quería escribir un comentario (ya que esta no es una respuesta definitiva), pero se hizo demasiado largo:
Voy a darle varios consejos generales, y podemos entrar en detalles para cada uno, si lo desea:
Recuerde que algunos de estos no son seguros o recomendables para las no importaciones (operación normal).
fuente
SET SESSION tx_isolation='READ-UNCOMMITTED';
(solo útil si importa con varios hilos en paralelo) y el comentario de @ypercube sobre la inserción en lotes. Tiene un ejemplo completo aquí: mysqlperformanceblog.com/2008/07/03/… Asegúrese de aprovechar todas las funciones de las últimas versiones de InnoDB: mysqlperformanceblog.com/2011/01/07/…La mayoría de los buenos consejos se han dado hasta ahora, pero sin muchas explicaciones para los mejores. Daré más detalles.
Primero, retrasar la creación del índice es bueno, con suficientes detalles en otras respuestas. No volveré sobre eso.
Un archivo de registro InnoDB más grande lo ayudará mucho (si está usando MySQL 5.6, ya que no es posible aumentarlo en MySQL 5.5). Está insertando 7 GB de datos, recomendaría un tamaño de registro total de al menos 8 GB (mantenga
innodb_log_files_in_group
su valor predeterminado (2) y aumenteinnodb_log_file_size
a 4 GB). Estos 8 GB no son exactos: debe tener al menos el tamaño de importación en el registro REDO y probablemente duplicar o cuadruplicar ese tamaño. El razonamiento detrás del tamaño del registro de InnoDB aumenta que cuando el registro esté casi lleno, InnoDB comenzará a vaciar agresivamente su grupo de búferes al disco para evitar que el registro se llene (cuando el registro está lleno, InnoDB no puede escribir ninguna base de datos hasta que algunos las páginas del grupo de búferes se escriben en el disco).Un archivo de registro InnoDB más grande lo ayudará, pero también debe insertarlo en el orden de las claves principales (ordene el archivo antes de insertarlo). Si inserta en orden de clave principal, InnoDB llenará una página, y luego otra, y así sucesivamente. Si no inserta en el orden de la clave principal, su próxima inserción podría terminar en una página que está llena e incurrirá en una "división de página". Esta división de página será costosa para InnoDB y ralentizará su importación.
Ya tiene un grupo de búferes tan grande como su RAM le permite y si su tabla no cabe, no hay mucho que pueda hacer, excepto comprar más RAM. Pero si su tabla cabe en el grupo de búferes, pero es mayor que el 75% de su grupo de búferes, puede intentar aumentar
innodb_max_dirty_pages_pct
a 85 o 95 durante la importación (el valor predeterminado es 75). Este parámetro de configuración le dice a InnoDB que comience a vaciar agresivamente el grupo de búferes cuando el porcentaje de páginas sucias alcanza este límite. Al aumentar este parámetro (y si tiene suerte con el tamaño de los datos), puede evitar las E / S agresivas durante la importación y retrasar esas E / S para más adelante.Tal vez (y esto es una suposición) importar sus datos en muchas transacciones pequeñas lo ayudará. No sé exactamente cómo se construye el registro REDO, pero si está almacenado en la memoria RAM (y en el disco cuando se necesitaría demasiada RAM) mientras la transacción está progresando, podría terminar con IO innecesarias. Puede intentar esto: una vez que su archivo esté ordenado, divídalo en muchos fragmentos (intente con 16 MB y otros tamaños) e impórtelos uno por uno. Esto también le permitiría controlar el progreso de su importación. Si no desea que sus datos sean parcialmente visibles para otro lector mientras realiza la importación, puede importar utilizando un nombre de tabla diferente, crear los índices más tarde y luego cambiar el nombre de la tabla.
Acerca de su disco híbrido SSD / 5400RPM, no sé acerca de ellos y cómo optimizar esto. 5400RPM parece lento para una base de datos, pero tal vez el SSD lo está evitando. Tal vez esté llenando la parte SSD de su disco con escrituras secuenciales en el registro REDO y la SSD está afectando el rendimiento. No lo sé.
Un mal consejo que no debe probar (o tener cuidado con) es el siguiente: no use multiproceso: será muy difícil de optimizar para evitar divisiones de página en InnoDB. Si desea utilizar varios subprocesos, inserte en diferentes tablas (o en diferentes particiones de la misma tabla).
Si está considerando varios subprocesos, tal vez tenga una computadora con varios zócalos (NUMA). En este caso, asegúrese de evitar el problema de locura de intercambio de MySQL .
Si está utilizando MySQL 5.5, actualice a MySQL 5.6: tiene la opción de aumentar el tamaño del registro REDO y tiene mejores algoritmos de descarga de agrupación de almacenamiento intermedio.
Buena suerte con tu importación.
fuente