La base de datos MySQL InnoDB se 'cuelga' en selectos


10

Estoy tratando de arreglar la configuración de MySQL en nuestro servidor. Los detalles de nuestra aplicación es que se almacenan muchos datos en una sola tabla (actualmente más de 300 millones de filas). Esta tabla se usa a menudo para inserciones (vienen todo el tiempo).

Cuando ejecuto una consulta de selección en esa tabla que lleva más de unos segundos, todas las inserciones (confirmaciones precisas) esperan el acceso a la tabla y hacen que nuestra aplicación no responda.

Hasta donde sé, InnoDB no realiza ningún bloqueo en la tabla cuando se ejecuta select. ¿Por qué es entonces la tabla de bloqueo select?

Traté de encontrar una razón con innotop pero no estoy seguro de cómo interpretar su salida y dónde buscar. Dime lo que necesitas y lo publicaré aquí.

+-----+---------+-----------+--------+---------+------+----------------+-----------------------------------------------------------------------------------------------------------------------------------+
| Id  | User    | Host      | db     | Command | Time | State          | Info                                                                                                                              |
+-----+---------+-----------+--------+---------+------+----------------+-----------------------------------------------------------------------------------------------------------------------------------+
|   1 | root    | localhost | dbname | Query   |   29 | NULL           | COMMIT                                                                                                                            | 
|   2 | root    | localhost | dbname | Query   |   30 | NULL           | COMMIT                                                                                                                            | 
|   4 | root    | localhost | dbname | Query   |   29 | NULL           | COMMIT                                                                                                                            | 
|   5 | root    | localhost | dbname | Query   |   29 | NULL           | COMMIT                                                                                                                            | 
|   6 | root    | localhost | dbname | Query   |   25 | NULL           | COMMIT                                                                                                                            | 
|   7 | root    | localhost | dbname | Query   |    0 | NULL           | show full processlist                                                                                                             | 
|  13 | user    | localhost | dbname | Query   |   25 | NULL           | COMMIT                                                                                                                            | 
|  38 | user    | localhost | dbname | Sleep   |    0 |                | NULL                                                                                                                              | 
|  39 | user    | localhost | dbname | Sleep   | 9017 |                | NULL                                                                                                                              | 
|  40 | user    | localhost | dbname | Query   |   33 | Sorting result | SELECT * FROM `large_table` WHERE (`large_table`.`hotspot_id` = 3000064)  ORDER BY discovered_at LIMIT 799000, 1000 | 
|  60 | user    | localhost | dbname | Sleep   | 1033 |                | NULL                                                                                                                              | 
|  83 | root    | localhost | dbname | Sleep   | 3728 |                | NULL                                                                                                                              | 
| 112 | root    | localhost | NULL   | Sleep   |    6 |                | NULL                                                                                                                              | 
+-----+---------+-----------+--------+---------+------+----------------+-----------------------------------------------------------------------------------------------------------------------------------+


=====================================
110824 12:24:24 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 19 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 1521117, signal count 1471216
Mutex spin waits 0, rounds 20647617, OS waits 239914
RW-shared spins 2119697, OS waits 1037149; RW-excl spins 505734, OS waits 218177
------------
TRANSACTIONS
------------
Trx id counter 0 412917332
Purge done for trx's n:o < 0 412917135 undo n:o < 0 0
History list length 48
Total number of lock structs in row lock hash table 5
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, process no 28363, OS thread id 1092766032
MySQL thread id 83, query id 3249941 localhost root
---TRANSACTION 0 412901582, not started, process no 28363, OS thread id 1144449360
MySQL thread id 60, query id 3677008 localhost user
---TRANSACTION 0 412917189, not started, process no 28363, OS thread id 1144314192
MySQL thread id 43, query id 3905773 localhost root
---TRANSACTION 0 412534255, not started, process no 28363, OS thread id 1092630864
MySQL thread id 39, query id 14279 localhost user
---TRANSACTION 0 412917331, not started, process no 28363, OS thread id 1144179024
MySQL thread id 38, query id 3908045 localhost user
---TRANSACTION 0 412917201, not started, process no 28363, OS thread id 1092495696
MySQL thread id 13, query id 3908257 localhost user
---TRANSACTION 0 412538821, not started, process no 28363, OS thread id 1092360528
MySQL thread id 7, query id 3908258 localhost root
show engine innodb status
---TRANSACTION 0 412917330, ACTIVE 6 sec, process no 28363, OS thread id 1144043856
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 2, query id 3907373 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917331, sees < 0 412917131
---TRANSACTION 0 412917328, ACTIVE 6 sec, process no 28363, OS thread id 1092225360
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 6, query id 3907345 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917329, sees < 0 412917131
---TRANSACTION 0 412917326, ACTIVE 6 sec, process no 28363, OS thread id 1091955024
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 4, query id 3907335 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917327, sees < 0 412917131
---TRANSACTION 0 412917324, ACTIVE 6 sec, process no 28363, OS thread id 1092090192
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 5, query id 3907328 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917325, sees < 0 412917131
---TRANSACTION 0 412917321, ACTIVE (PREPARED) 7 sec, process no 28363, OS thread id 1143908688 preparing
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 1, query id 3907125 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917322, sees < 0 412917131
---TRANSACTION 0 412917131, ACTIVE 20 sec, process no 28363, OS thread id 1074075984, thread declared inside InnoDB 111
mysql tables in use 1, locked 0
MySQL thread id 40, query id 3904958 localhost user Sorting result
SELECT * FROM `large_table` WHERE (`large_table`.`hotspot_id` = 3000064)  ORDER BY discovered_at LIMIT 848000, 1000
Trx read view will not see trx with id >= 0 412917132, sees < 0 412917132
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 1; buffer pool: 0
3510225 OS file reads, 284998 OS file writes, 202897 OS fsyncs
1.05 reads/s, 21299 avg bytes/read, 8.10 writes/s, 7.58 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 275, free list len 13392, seg size 13668,
489950 inserts, 491830 merged recs, 10986 merges
Hash table size 8850487, used cells 8127172, node heap has 32697 buffer(s)
71914.53 hash searches/s, 8701.91 non-hash searches/s
---
LOG
---
Log sequence number 157 3331524445
Log flushed up to   157 3331521939
Last checkpoint at  157 3326072846
1 pending log writes, 0 pending chkp writes
199025 log i/o's done, 7.53 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 4788954432; in additional pool allocated 1048576
Buffer pool size   262144
Free buffers       0
Database pages     229447
Modified db pages  1439
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 7453325, created 14887, written 118658
1.37 reads/s, 0.11 creates/s, 0.53 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
1 queries inside InnoDB, 0 queries in queue
7 read views open inside InnoDB
Main thread process no. 28363, id 1091684688, state: flushing log
Number of rows inserted 1093064, updated 249134, deleted 1405, read 1115880534
7.89 inserts/s, 2.47 updates/s, 0.05 deletes/s, 80953.21 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

EDITAR:

Gracias por aclarar esto.

Entonces tengo que dividir mi pregunta en dos casos ahora.

  1. ¿Es normal que el bloqueo en esta única tabla haga que toda mi aplicación se 'cuelgue'? ¿No debería DB responder las consultas a otras tablas? Tal vez un poco de búfer está configurado demasiado bajo?

  2. ¿Cambiar esta tabla a MyISAM ayuda? No necesito transacciones en esta tabla en absoluto. ¿No habrá otros bloqueos en una situación como esa (selección larga + muchas inserciones rápidas)?

EDIT2:

Así es como se ven las consultas de inserción:

INSERT INTO `large_table` (`device_address`, `hotspot_id`, `minute`, `created_at`, `updated_at`, `discovered_with_hci`, `hour`, `rssi`, `day`, `device_class`, `discovered_at`) VALUES('10:40:03:90:10:40', 3000008, 1, '2011-08-22 05:01:08', '2011-08-22 05:01:08', -1, 5, -79, '2011-08-22 05:01:01', '0', '2011-08-22 05:01:01')

Eso es lo que los índices se definen en él:

+-------------+------------+----------------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table       | Non_unique | Key_name                                     | Seq_in_index | Column_name         | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------------+------------+----------------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+
| large_table |          0 | PRIMARY                                      |            1 | id                  | A         |    92396334 |     NULL | NULL   |      | BTREE      |         | 
| large_table |          1 | index_large_table_on_discovered_with_hci     |            1 | discovered_with_hci | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_hotspot_id              |            1 | hotspot_id          | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_day_and_hour_and_minute |            1 | day                 | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_day_and_hour_and_minute |            2 | hour                | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_day_and_hour_and_minute |            3 | minute              | A         |      537187 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_created_at              |            1 | created_at          | A         |     8399666 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_rssi                    |            1 | rssi                | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
+-------------+------------+----------------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+

EDITAR 3:

¿Por qué durante tales consultas completas mi aplicación no responde? ¿No debería afectar solo a esa 'tabla_grande'?

Tal vez algo está mal con mi configuración de MySQL? El servidor es un Xeon 2 GHz de 4 núcleos con 16 GB de RAM. Ejecuta la aplicación MySQL + Rails

Mis parámetros de configuración:

skip-external-locking
key_buffer              = 64M
max_allowed_packet      = 16M
thread_stack            = 128K
thread_cache_size       = 8
query_cache_size        = 32M
tmp_table_size          = 64M
max_heap_table_size     = 64M
table_cache             = 256
read_rnd_buffer_size    = 512K
sort_buffer_size        = 2M

myisam-recover          = BACKUP
max_connections         = 200

query_cache_limit       = 1M

long_query_time = 200

max_binlog_size         = 100M

innodb_buffer_pool_size = 4G
safe-updates
max_join_size=100000000

El script de Mysqltuner solo sugiere:

long_query_time (<= 10)
innodb_buffer_pool_size (>= 62G)

Por favor agregue la salida de show engine innodb status;.
quanta

En innotop puedes presionar L para ver las cerraduras. ¿Cómo establece su aplicación una conexión? JDBC? ¿Qué nivel de DefaultTransactionIsolation está utilizando?
HTTP500

Es una aplicación RoR, así que supongo que es predeterminada para MySQL (¿puedo verificarla de alguna manera mediante una consulta SQL?). innotop no muestra ningún bloqueo mientras se ejecuta esta selección.
kaczor1984

@ kaczor1984 Puede verificar el nivel de aislamiento predeterminado haciendo: mostrar variables como 'tx_isolation'; consulta. El valor predeterminado es REPEATABLE READ. Tenga en cuenta que MVCC solo funciona con REPEATABLE READ y READ COMMITTED. No estoy seguro de cuál es la solución a su problema, pero la respuesta de RolandoMySQLDBA fue informativa.
HTTP500

Actualicé mi respuesta con un análisis de la consulta en el ID de proceso 40
RolandoMySQLDBA

Respuestas:


15

Observe atentamente la lista de procesos y el "estado del motor de presentación del motor". Que ves ???

Las ID de proceso 1,2,4,5,6,13 intentan ejecutar COMMIT.

¿Quién está sosteniendo todo? El ID de proceso 40 está ejecutando una consulta en large_table.

La ID de proceso 40 ha estado ejecutándose durante 33 segundos. Las ID de proceso 1, 2, 4, 5, 6, 13 han estado ejecutándose menos de 33 segundos. La ID de proceso 40 está procesando algo. Cuál es la soporte ???

En primer lugar, la consulta está golpeando el índice agrupado de large_table a través de MVCC .

Dentro de los ID de proceso 1, 2, 4, 5, 6, 13 hay filas que tienen datos MVCC que protegen su aislamiento de transacción. El ID de proceso 40 tiene una consulta que marcha a través de filas de datos. Si hay un índice en el campo hotspot_id, esa clave + la clave de la fila real del índice agrupado debe realizar un bloqueo interno. (Nota: por diseño, todos los índices no únicos en InnoDB llevan tanto su clave (la columna que quería indexar) + una clave de índice agrupada). Este escenario único es esencialmente Fuerza imparable y objeto inamovible.

En esencia, los COMPROMISOS deben esperar hasta que sea seguro aplicar cambios en large_table. Su situación no es única, no es un fenómeno único, no es raro.

De hecho, respondí tres preguntas como esta en el DBA StackExchange. Las preguntas fueron enviadas por la misma persona relacionada con el mismo problema. Mis respuestas no fueron la solución, pero ayudaron al solicitante a llegar a su propia conclusión sobre cómo manejar su situación.

Además de esas respuestas, respondí la pregunta de otra persona sobre puntos muertos en InnoDB con respecto a SELECT .

Espero que mis publicaciones anteriores sobre este tema ayuden a aclarar lo que te estaba sucediendo.

ACTUALIZACIÓN 2011-08-25 08:10 EDT

Aquí está la consulta del ID de proceso 40

SELECT * FROM `large_table`
WHERE (`large_table`.`hotspot_id` = 3000064)
ORDER BY discovered_at LIMIT 799000, 1000;

Dos observaciones:

  • Estás haciendo 'SELECCIONAR *' ¿necesitas buscar cada columna? Si solo necesita columnas específicas, debe etiquetarlas porque la tabla temporal de 1000 filas podría ser más grande de lo que realmente necesita.

  • Las cláusulas WHERE y ORDER BY generalmente revelan problemas de rendimiento o hacen que el diseño de la mesa brille. Debe crear un mecanismo que acelere la recopilación de claves antes de recopilar datos.

A la luz de estas dos observaciones, hay dos cambios principales que debe hacer:

CAMBIO PRINCIPAL # 1: Refactorizar la consulta

Rediseñar la consulta para que

  1. las claves se recopilan del índice
  2. solo 1000 o ellos son recolectados
  3. unido de nuevo a la mesa principal

Aquí está la nueva consulta que hace estas tres cosas

SELECT large_table.* FROM
large_table INNER JOIN
(
    SELECT hotspot_id,discovered_at
    FROM large_table
    WHERE hotspot_id = 3000064
    ORDER BY discovered_at
    LIMIT 799000,1000
) large_table_keys
USING (hotspot_id,discovered_at);

La subconsulta large_table_keys reúne las 1000 claves que necesita. El resultado de la subconsulta es entonces INNER JOINed a large_table. Hasta ahora, las claves se recuperan en lugar de filas enteras. Eso sigue siendo 799,000 filas para leer. Hay una mejor manera de obtener esas llaves, lo que nos lleva a ...

CAMBIO PRINCIPAL # 2: Crear índices que admitan la consulta refactorizada

Dado que la consulta refactorizada solo presenta una subconsulta, solo necesita hacer un índice. Aquí está ese índice:

ALTER TABLE large_table ADD INDEX hotspot_discovered_ndx (hotspot_id,discovered_at);

¿Por qué este índice en particular? Mira la cláusula WHERE. El hotspot_id es un valor estático. Esto hace que todos los hotspot_ids formen una lista secuencial en el índice. Ahora, mire la cláusula ORDER BY. La columna discover_at es probablemente un campo DATETIME o TIMESTAMP.

El orden natural que esto presenta en el índice es el siguiente:

  • El índice presenta una lista de hostpot_ids
  • Cada hotspot_id tiene una lista ordenada de campos descubiertos en

Hacer este índice también elimina la clasificación interna de las tablas temporales.

Ponga estos dos cambios principales en su lugar y verá una diferencia en el tiempo de ejecución.

Darle una oportunidad !!!

ACTUALIZACIÓN 2011-08-25 08:15 EDT

Miré tus índices. Aún necesita crear el índice que sugerí.


Gracias por una gran explicación de cómo funciona. Me temo que no puedo entender cómo evitar situaciones como esa. Las inserciones tienen que modificar los índices y seleccionar debe usar el índice en hotspot_id y discover_at. Me alegraría que también pudiera responder a mi 'idea' de cambiar a MyISAM.
kaczor1984

En realidad, usar MyISAM podría empeorar las cosas porque cada INSERTAR, ACTUALIZAR y ELIMINAR en MyISAM activa un bloqueo de tabla completo. Incluso si usa LOW_PRIORITY INSERTs o INSERT DELAYED, aún se encontrarán bloqueos de tabla completos. Debe explorar la consulta en sí porque, independientemente del motor de almacenamiento, las consultas se pueden ajustar en torno a dichos obstáculos. Por lo menos, un nuevo algoritmo completo puede ser requerido.
Veré

He actualizado mi primera publicación para que pueda ver las consultas e índices de inserción en esta tabla.
kaczor1984

Actualicé mi respuesta con un análisis de la consulta en el ID de proceso 40
RolandoMySQLDBA

44
usted señor es un héroe entre los hombres por sus largas respuestas mysql
Mike

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.