2010-09-15 16 views
31

Este es un problema bastante simple. Insertar datos en la tabla normalmente funciona bien, excepto en algunas ocasiones en las que la consulta de inserción tarda unos segundos. (Estoy no intentando insertar datos a granel.) Por lo tanto, configuré una simulación para el proceso de inserción para averiguar por qué la consulta de inserción ocasionalmente tarda más de 2 segundos en ejecutarse. Joshua sugirió que el archivo de índice puede estar siendo ajustado; Eliminé el id (campo de clave principal), pero la demora aún ocurre.¿Por qué una consulta de inserción ocasionalmente toma tanto tiempo en completarse?

Tengo una tabla MyISAM: daniel_test_insert (esta tabla se inicia completamente vacío):

create table if not exists daniel_test_insert ( 
    id int unsigned auto_increment not null, 
    value_str varchar(255) not null default '', 
    value_int int unsigned default 0 not null, 
    primary key (id) 
) 

que insertar datos en ella, ya veces una consulta de inserción toma> 2 segundos para funcionar. No hay lecturas en esta tabla: solo se escribe, en serie, mediante un único programa de subprocesos.

Ejecuté exactamente la misma consulta 100.000 veces para encontrar por qué la consulta ocasionalmente lleva mucho tiempo. Hasta ahora, parece ser una ocurrencia aleatoria.

Esta consulta, por ejemplo, tomó 4.194 segundos (un tiempo muy largo para una inserción):

Query: INSERT INTO daniel_test_insert SET value_int=12345, value_str='afjdaldjsf aljsdfl ajsdfljadfjalsdj fajd as f' - ran for 4.194 seconds 
status    | duration | cpu_user | cpu_system | context_voluntary | context_involuntary | page_faults_minor 
starting    | 0.000042 | 0.000000 | 0.000000 | 0     | 0     | 0     
checking permissions | 0.000024 | 0.000000 | 0.000000 | 0     | 0     | 0     
Opening tables  | 0.000024 | 0.001000 | 0.000000 | 0     | 0     | 0     
System lock   | 0.000022 | 0.000000 | 0.000000 | 0     | 0     | 0     
Table lock   | 0.000020 | 0.000000 | 0.000000 | 0     | 0     | 0     
init     | 0.000029 | 0.000000 | 0.000000 | 1     | 0     | 0     
update    | 4.067331 | 12.151152 | 5.298194 | 204894   | 18806    | 477995   
end     | 0.000094 | 0.000000 | 0.000000 | 8     | 0     | 0     
query end   | 0.000033 | 0.000000 | 0.000000 | 1     | 0     | 0     
freeing items  | 0.000030 | 0.000000 | 0.000000 | 1     | 0     | 0     
closing tables  | 0.125736 | 0.278958 | 0.072989 | 4294    | 604     | 2301    
logging slow query | 0.000099 | 0.000000 | 0.000000 | 1     | 0     | 0     
logging slow query | 0.000102 | 0.000000 | 0.000000 | 7     | 0     | 0     
cleaning up   | 0.000035 | 0.000000 | 0.000000 | 7     | 0     | 0 

(Esta es una versión abreviada de los mismos Mostrar perfil, me echaron fuera las columnas que eran todos iguales a cero.)

Ahora la actualización tiene un número increíble de interruptores de contexto y fallas de página menores. Opened_tables aumenta alrededor de 1 por cada 10 segundos en esta base de datos (no corriendo de table_cache espacio)

Estadísticas:

  • MySQL 5.0.89

  • Hardware: 32 gigas de RAM/8 núcleos a 2,66 GHz; raid 10 Discos duros SCSI (SCSI II ???)

  • He tenido los discos duros y controlador de ataque consultados: No se han informado errores. Las CPU tienen un 50% de inactividad.

  • iostat -x 5 (informes de menos de 10% de utilización de discos duros) superior medio de carga de informe sobre 10 durante 1 minuto (lo normal en nuestra máquina db)

  • espacio de intercambio ha 156K usado (32 gigas de ram)

No puedo saber qué está causando este retraso de rendimiento. Esto NO ocurre en nuestros esclavos de carga baja, solo en nuestro maestro de carga alta. Esto también ocurre con la memoria y las tablas innodb. ¿Alguien tiene alguna sugerencia? (Este es un sistema de producción, así que nada exótico!)

+0

¿Funcionan bien otras tablas/bases de datos? ¿Podría haber un disco dañado (errores de E/S en system.log)? ¿Cuánto tiempo toma la consulta desde la línea de comando mysql? ¿Qué pasa con mysql mydb

+0

No, tenemos este problema en otras tablas. Creé una tabla de muestra y simplemente la dejé para ver qué sucedía. Si uso mysql mydb Daniel

+1

¿A qué te refieres con "Esta misma fila, 100,000 veces"? ¿Quiere decir que una inserción simple tarda 2 segundos, o hacer la misma inserción 100.000 veces lleva 2 segundos? –

Respuesta

19

He notado el mismo fenómeno en mis sistemas. Las consultas que normalmente toman un milisegundo tomarán de repente 1-2 segundos. Todos mis casos son simples, INSERT/UPDATE/REPLACE de una sola tabla --- no en ningún SELECT. Sin carga, bloqueo o acumulación de hilo es evidente.

He sospechado que se debe a borrar páginas sucias, cambios de enrojecimiento en el disco, o algún mutex oculto, pero aún tengo que limitarlo.

también descartó

  • carga del servidor - ninguna correlación con alta carga
  • motor - sucede con InnoDB/MyISAM/Memoria
  • MySQL Query Cache - sucede ya sea dentro o off
  • rotaciones Log - ninguna correlación en eventos

El ONL La otra observación que tengo en este momento se deriva del hecho de que estoy ejecutando el mismo DB en varias máquinas. Tengo una aplicación de lectura pesada, así que estoy usando un entorno con replicación: la mayor parte de la carga está en los esclavos. Me di cuenta de que a pesar de que hay una carga mínima en el maestro, el fenómeno ocurre más allí. Aunque no veo problemas de bloqueo, tal vez Innodb/Mysql tenga problemas con la concurrencia (de subprocesos). Recuerde que las actualizaciones en el esclavo serán de un solo hilo.

MySQL Verion 5.1.48

actualización

creo que tengo una ventaja para el problema en mi caso. En algunos de mis servidores, noté este fenómeno en más que los demás. Viendo lo que era diferente entre los diferentes servidores y retocando las cosas, fui llevado al MySQL innodb system variableinnodb_flush_log_at_trx_commit.

me encontré con el doctor un poco difícil de leer, pero innodb_flush_log_at_trx_commit puede tomar los valores de 1,2,0:

  • Para 1, el búfer de registro se vacía a el archivo de registro para cada confirmación, y el archivo de registro se vacía en el disco por cada confirmación.
  • Para 2, el búfer de registro se vacía a el archivo de registro para cada confirmación, y el archivo de registro se vacía en el disco aproximadamente cada 1-2 segundos.
  • Para 0, el búfer de registro se vacía a el archivo de registro cada segundo, y el archivo de registro se vacía al disco cada segundo.

Efectivamente, en el orden (1,2,0), según lo informado y documentado, se supone que debe obtener un mayor rendimiento en el comercio para un mayor riesgo.

Habiendo dicho eso, encontré que los servidores con innodb_flush_log_at_trx_commit=0 tenían un peor rendimiento (es decir, que tenían 10-100 veces más "actualizaciones largas") que los servidores con innodb_flush_log_at_trx_commit=2. Además, las cosas mejoraron inmediatamente en las malas instancias cuando lo cambié a 2 (tenga en cuenta que puede cambiarlo sobre la marcha).

Entonces, mi pregunta es, ¿cuál es la tuya establecida? Tenga en cuenta que no culpo a este parámetro, sino que resalto que su contexto está relacionado con este problema.

+0

Recientemente me actualicé a 5.1.48. Creo que fue el caché de consultas. Lo apagué y las cosas comenzaron a funcionar mucho mejor. – Daniel

+2

Tuve un problema similar hace un año cuando mi query_cache era demasiado grande, por lo que cuando se invalidaba una gran cantidad de datos, todo el servidor se bloqueaba por hasta 30 segundos. En esa nota, ver: http://dom.as/2009/07/08/query-cache-tuning/ – Riedsio

+0

innodb_flush_log_at_trx_commit = 2 también se trabajó para mí. – metdos

1

Tuve este problema usando tablas INNODB. (y los índices INNODB son aún más lentos para reescribir que MYISAM)

Supongo que está haciendo muchas otras consultas en otras tablas, por lo que el problema sería que MySQL debe manejar las escrituras en archivos que se agrandan y necesita asignar espacio adicional para esos archivos.

Si utiliza tablas MyISAM sugiero encarecidamente el uso de

LOAD DATA INFILE 'file-on-disk' INTO TABLE `tablename` 

comando; MYISAM es sensacionalmente rápido con esto (incluso con claves principales) y el archivo puede formatearse como csv y puede especificar los nombres de columna (o puede poner NULL como el valor para el campo de autoincrement).

View MYSQL doc here.

1

La primera sugerencia que le daría, es deshabilitar la funcionalidad de confirmación automática y luego confirmarla manualmente.

LOCK TABLES a WRITE; 
... DO INSERTS HERE 
UNLOCK TABLES; 

Esto beneficia el rendimiento porque el búfer de índice se enjuaga en el disco una sola vez, después de que se hayan completado todas las instrucciones INSERT. Normalmente, habrá tantas descargas de búfer de índice como INSERT.

Pero lo mejor es que puedes hacer, y si eso es posible en tu aplicación, haces una inserción masiva con una sola selección.

Esto se realiza a través de Vector Binding y es la manera más rápida que puede ir.

Instead 
of: 
"INSERT INTO tableName values()" 
DO 
"INSERT INTO tableName values(),(),(),().......(n) " , 

Pero considere esta opción solo si la vinculación del vector de parámetros es posible con su controlador mysql que esté utilizando.

De lo contrario, tendería a la primera posibilidad y BLOQUEAR la tabla por cada 1000 inserciones. No lo bloquee para inserciones de 100k, porque obtendrá un desbordamiento de búfer.

+0

¿Qué lenguaje de programación estás usando? Tal vez puedo darte un ejemplo de cómo hacerlo. – BitKFu

+0

Estoy usando ruby. Sin embargo, estoy haciendo el mismo inserto 100.000 veces porque algunos toman tanto tiempo. Estoy tratando de encontrar la razón por la que algunos de ellos demoran tanto. – Daniel

+0

¿Intentó BLOQUEAR, DESBLOQUEAR la tabla y comprobar cómo se comportaba el rendimiento? – BitKFu

0

Leer esto en rendimiento myisam: http://adminlinux.blogspot.com/2010/05/mysql-allocating-memory-for-caches.html

Buscar:

'El tamaño del bloque de claves de MyISAM El tamaño de la clave de bloque es importante' (sin las comillas simples), esto podría ser lo que está pasando. Creo que arreglaron algunos de estos tipos de problemas con 5.1

+0

el problema es que esto aún ocurre incluso si se eliminan las claves. IE, una tabla sin ningún índice. – Daniel

+0

comprensible, (creo) que MySQL bloquea el espacio para escribir registros.algo así (por ejemplo, porque no sé) dice que bloquea el espacio suficiente para escribir en 5 registros, después de usar el espacio MySQL necesita reservar otro bloque de espacio para el siguiente conjunto de registros. Este podría ser el tiempo de ejecución adicional que está sucediendo. –

1

¿Puedes crear una tabla más con 400 columnas (no nulas) y ejecutar tu prueba nuevamente? Si el número de inserciones lentas aumenta, esto podría indicar que MySQL está perdiendo el tiempo escribiendo sus registros. (No sé cómo funciona, pero puede estar alocando más bloques, o moviendo algo para evitar la fragmentación .... realmente no sé)

+0

humm interesante, lo intentaré. – Daniel

+0

Agregué más columnas, y encontré que las escrituras eran bastante lentas. Me pregunto si la demora tiene algo que ver con la escritura de datos en el disco duro. Nuestros discos duros parecen tener capacidad de rendimiento. – Daniel

+0

es decir, 34 escrituras por segundo. ¡Lo cual para una tabla no indexada es bastante lento! – Daniel

0

¿Puedes verificar las estadísticas en el subsistema del disco? ¿Está saturada la E/S? Esto suena como un trabajo interno de DB que va a enjuagar cosas en el disco/registro.

+0

Bueno, no puedo obtener mediciones precisas en IO. Pero he ejecutado "iostat -x 5" y no he visto nada de 5-10% mientras corría. Puede haber picos temporales que pueden estar ocultos debido al "promedio". No tengo idea de cómo consultar el sistema para obtener mediciones más finas que no sean las de vmstat, que tampoco arrojó información útil. – Daniel

+0

He visto problemas en SQLServer en los que el rendimiento hace cosas extrañas, cuando no es mi código (lol) usualmente es el subsistema de disco. SQLServer tiene un parámetro de intervalo de recuperación en el que SQLServer vacía cosas en el disco. Si está presionando realmente en las inserciones y se alcanza el intervalo de recuperación, el rendimiento es un poco difícil para la aplicación, ya que algunas inserciones llevan mucho tiempo y en realidad no tiene nada que ver con la inserción, sino con algunas internas. mantenimiento en SQLServer - también podría ser que el disco del canal - husos y/o controladores de disco están al 100% – bigtang

0

Para comprobar si el disco se está comportando mal, y si estás en Windows, puede crear un archivo por lotes que crea cmd 10.000 archivos:

@echo OFF 
FOR /L %%G IN (1, 1, 10000) DO TIME /T > out%%G.txt 

guardarlo en un directorio temporal, como prueba. cmd

Habilitar extensiones de comando se ejecuta con el CMD/e: ON parámetro

CMD.exe /E:ON 

a continuación, ejecute el lote y ver si el tiempo entre el primer y el último archivo de salida difieren en segundos o minu tes

En Unix/Linux puede escribir un script de shell similar.

0

Por casualidad, ¿hay una unidad SSD en el servidor? Algunas unidades SSD sufren de 'studder', que podría causar su síntoma.

En cualquier caso, trataría de averiguar si la demora está ocurriendo en MySQL o en el subsistema del disco.

¿Qué sistema operativo es su servidor y en qué sistema de archivos están los datos de MySQL?

+0

Tenemos verdaderos discos duros SCSI atacados. Estamos utilizando CentOS 5.4 y xfs para el sistema de archivos/instantáneas de archivos. He pensado en usar FusionIO, pero nunca ganó tracción. – Daniel

0

Actualizamos a MySQL 5.1 y durante este evento, la caché de consultas se convirtió en un problema con una gran cantidad de "elementos de liberación?" estados de hilos Luego eliminamos el caché de consultas.

O la actualización a MySQL 5.1 o la eliminación de la caché de consultas resolvió este problema.

FYI, para lectores futuros.

-Daniel

1

llegamos a exactamente la misma cuestión e informó aquí: http://bugs.mysql.com/bug.php?id=62381

Estamos utilizando 01.05.52 y no tenemos solución todavía. Es posible que necesitemos apagar el control de calidad para evitar este golpe de rendimiento.

0

si está utilizando la inserción múltiple en uno usando for loop, entonces descanse después de cada ciclo utilizando la función sleep ("tiempo en segundos") de PHP.

Cuestiones relacionadas