2012-02-02 17 views
9

Recientemente cambié mis tablas de proyecto a InnoDB (pensando que las relaciones serían algo bueno tener). Estoy usando un script PHP para indexar alrededor de 500 productos a la vez.InnoDB se inserta muy lento y ralentizando

Palabras/asociación ids tabla que almacena:

CREATE TABLE `windex` (
`word` varchar(64) NOT NULL, 
`wid` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`count` int(11) unsigned NOT NULL DEFAULT '1', 
PRIMARY KEY (`wid`), 
UNIQUE KEY `word` (`word`) 
) ENGINE=InnoDB AUTO_INCREMENT=324551 DEFAULT CHARSET=latin1 

Otra tabla almacena los ID de producto/palabra asociaciones Identificación:

CREATE TABLE `indx_0` (
`wid` int(7) unsigned NOT NULL, 
`pid` int(7) unsigned NOT NULL, 
UNIQUE KEY `wid` (`wid`,`pid`), 
KEY `pid` (`pid`), 
CONSTRAINT `indx_0_ibfk_1` FOREIGN KEY (`wid`) REFERENCES `windex` (`wid`) ON DELETE CASCADE ON UPDATE CASCADE, 
CONSTRAINT `indx_0_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `product` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

El script se ensayó usando MyISAM y que indexa productos relativamente rápido (mucho , mucho más rápido que InnoDB). La primera vez que ejecuté InnoDB fue ridículamente lento, pero después de anidar más valores juntos terminé acelerando mucho (pero no lo suficiente).

Supongo que innodb sería mucho más rápido para este tipo de cosas debido a bloqueos rowlevel, pero ese no es el caso.

construyo una consulta que se ve algo como:

SELECT 
title,keywords,upc,... 
FROM product 
WHERE indexed = 0 
LIMIT 500 

puedo crear un bucle y llenar una matriz con todas las palabras que necesitan ser añadido a Windex y todos los pares de palabra/Identificación del producto id que necesitan para ser agregado a indx_0.

Debido a que innodb sigue aumentando mis valores de autoincremento cada vez que hago un "REEMPLAZAR" o "INSERTAR IGNORAR" que falla debido a valores duplicados, necesito asegurarme de que los valores que agrego no existan. Para hacer que por primera vez seleccionar todos los valores que existen, utilizando una consulta como por ejemplo:

SELECT wid,word 
FROM windex 
WHERE 
word = "someword1" or word = "someword2" or word = "someword3" ... ... 

Entonces puedo filtrar mi batalla contra los resultados que existen por lo que todas las nuevas palabras que agrego son 100% nuevo.

Esto toma alrededor del 20% del tiempo total de ejecución. El otro 80% se usa para agregar los valores de par en indx_0, para lo cual hay muchos más valores.

Aquí hay un ejemplo de lo que obtengo.

0.4806 segundos para seleccionar productos. (0,4807 sec en total).
0.0319 segundos para reunir 500 elementos. (0.5126 sec en total).
5.2396 segundos para seleccionar los valores de windex para la comparación. (5,7836 segundos en total).
1.8986 segundos para actualizar la cuenta. (7.6822 segundos en total).
0.0641 segundos para agregar 832 registros de windex. (7.7464 segundos en total).
17.2725 segundos para agregar el índice de 3435 pares pid/wid. (25.7752 segundos en total).
La operación tomó 26.07 segundos para indexar 500 productos.

Los 3435 pares están siendo ejecutados todos en una sola consulta, tales como:

INSERT INTO indx_0(pid,wid) 
VALUES (1,4),(3,9),(9,2)... ... ... 

¿Por qué es InnoDB mucho más lento que MyISAM en mi caso?

+0

¿La idea del índice de palabras es crear algún tipo de funcionalidad de búsqueda? Si ese es el caso, hayas hecho eso, echa un vistazo a un motor de búsqueda real como solr o mysql fulltext search por ejemplo. No puede superar tales tareas específicas. –

Respuesta

13

InnoDB proporciona una estructura de claves más compleja que MyIsam (FOREIGN KEYS) y la regeneración de claves es muy lenta en InnoDB.Debería encerrar todas las instrucciones de actualización/inserción en una transacción (en realidad son bastante rápidas en InnoDB, una vez que tuve alrededor de 300 000 consultas insertadas en la tabla InnoDb con 2 índices y tardé alrededor de 30 minutos, una vez encerré cada 10 000 inserciones en BEGIN TRANSACTION y COMMIT tomó menos de 2 minutos).

me recomiendan usar:

BEGIN TRANSACTION; 
SELECT ... FROM products; 
UPDATE ...; 
INSERT INTO ...; 
INSERT INTO ...; 
INSERT INTO ...; 
COMMIT; 

Esto hará que InnoDB para actualizar los índices de una sola vez no pocos cientos de veces.

Avísame si ha funcionado

+0

Debería aportar algunas mejoras, estoy seguro. Tengo un problema similar, Vyktor. Parece que esto funcionará. Gracias -Un día – Uday

+2

Estaba teniendo un problema en un cursor que este fijo (de 90 segundos a 0.9!) Poco a poco estoy aprendiendo lo que se requiere de InnoDB –

+0

@Vyktor, En cuanto a * "Encerré cada 10 000 insertos en' BEGIN TRANSACTION' y 'COMMIT 'tomó menos de 2 minutos" *, ¿Por qué se divide en lotes de 10k? ¿Por qué no adjuntar ** todas las declaraciones ** dentro de una sola transacción? – Pacerier

4

tuve un problema similar y parece InnoDB tiene por defecto activado innodb_flush_log_at_trx_commit la cual elimina todos los/actualización consulta de inserción en el archivo de registro de HDD. La velocidad de escritura de su disco duro es un cuello de botella para este proceso.

Así que trate de modificar el archivo de configuración de MySQL

`innodb_flush_log_at_trx_commit = 0` 

Reiniciar servicio MySQL.

Experimenté x100 aceleración en las inserciones.

+1

Tenga en cuenta que la seguridad de las transacciones se pierde aplicando esta opción ... Si pierde energía después de decirle al cliente que se hizo, pero antes de que realmente se escriba en el disco significará que se perderá para siempre. – Cine