Problema
Una instancia de MySQL 5.6.20 que ejecuta (principalmente solo) una base de datos con tablas InnoDB exhibe paradas ocasionales para todas las operaciones de actualización durante un período de 1 a 4 minutos con todas las consultas INSERT, UPDATE y DELETE restantes en el estado "Fin de consulta". Esto obviamente es muy desafortunado. El registro de consulta lenta de MySQL está registrando incluso las consultas más triviales con tiempos de consulta locos, cientos de ellas con la misma marca de tiempo correspondiente al punto en el tiempo en que se resolvió el bloqueo:
# Query_time: 101.743589 Lock_time: 0.000437 Rows_sent: 0 Rows_examined: 0
SET timestamp=1409573952;
INSERT INTO sessions (redirect_login2, data, hostname, fk_users_primary, fk_users, id_sessions, timestamp) VALUES (NULL, NULL, '192.168.10.151', NULL, 'anonymous', '64ef367018099de4d4183ffa3bc0848a', '1409573850');
Y las estadísticas del dispositivo muestran un aumento, aunque no una carga de E / S excesiva en este período de tiempo (en este caso, las actualizaciones se estancaron 14:17:30 - 14:19:12 según las marcas de tiempo de la declaración anterior):
# sar -d
[...]
02:15:01 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
02:16:01 PM dev8-0 41.53 207.43 1227.51 34.55 0.34 8.28 3.89 16.15
02:17:01 PM dev8-0 59.41 137.71 2240.32 40.02 0.39 6.53 4.04 24.00
02:18:01 PM dev8-0 122.08 2816.99 1633.44 36.45 3.84 31.46 1.21 2.88
02:19:01 PM dev8-0 253.29 5559.84 3888.03 37.30 6.61 26.08 1.85 6.73
02:20:01 PM dev8-0 101.74 1391.92 2786.41 41.07 1.69 16.57 3.55 36.17
[...]
# sar
[...]
02:15:01 PM CPU %user %nice %system %iowait %steal %idle
02:16:01 PM all 15.99 0.00 12.49 2.08 0.00 69.44
02:17:01 PM all 13.67 0.00 9.45 3.15 0.00 73.73
02:18:01 PM all 10.64 0.00 6.26 11.65 0.00 71.45
02:19:01 PM all 3.83 0.00 2.42 24.84 0.00 68.91
02:20:01 PM all 20.95 0.00 15.14 6.83 0.00 57.07
La mayoría de las veces, noto en el registro lento de mysql que el bloqueo de consultas más antiguo es un INSERT en una tabla de gran tamaño (~ 10 M filas) con una clave primaria VARCHAR y un índice de búsqueda de texto completo:
CREATE TABLE `files` (
`id_files` varchar(32) NOT NULL DEFAULT '',
`filename` varchar(100) NOT NULL DEFAULT '',
`content` text,
PRIMARY KEY (`id_files`),
KEY `filename` (`filename`),
FULLTEXT KEY `content` (`content`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Investigaciones posteriores (es decir, SHOW ENGINE INNODB STATUS) han demostrado que, de hecho, siempre es una actualización de una tabla que utiliza índices de texto completo que está causando el bloqueo. La sección respectiva de TRANSACCIONES de "SHOW ENGINE INNODB STATUS" tiene entradas como estas dos para las transacciones en ejecución más antiguas:
---TRANSACTION 162269409, ACTIVE 122 sec doing SYNC index
6 lock struct(s), heap size 1184, 0 row lock(s), undo log entries 19942
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_1" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_2" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_3" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_4" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_5" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_6" trx id 162269409 lock mode IX
---TRANSACTION 162269408, ACTIVE (PREPARED) 122 sec committing
mysql tables in use 1, locked 1
1 lock struct(s), heap size 360, 0 row lock(s), undo log entries 1
MySQL thread id 165998, OS thread handle 0x7fe0e239c700, query id 91208956 192.168.10.153 root query end
INSERT INTO files (id_files, filename, content) VALUES ('f19e63340fad44841580c0371bc51434', '1237716_File_70380a686effd6b66592bb5eeb3d9b06.doc', '[...]
TABLE LOCK table `vw`.`files` trx id 162269408 lock mode IX
Entonces, hay una acción pesada de índice de texto completo allí ( doing SYNC index
) que detiene TODAS LAS actualizaciones SUBSECUENTES en CUALQUIER tabla.
Según los registros, parece que el undo log entries
número doing SYNC index
avanza a ~ 150 / s hasta llegar a 20,000, momento en el que se realiza la operación.
El tamaño FTS de esta tabla específica es bastante impresionante:
# du -c FTS_000000000000224a_00000000000036b9_*
614404 FTS_000000000000224a_00000000000036b9_INDEX_1.ibd
2478084 FTS_000000000000224a_00000000000036b9_INDEX_2.ibd
1576964 FTS_000000000000224a_00000000000036b9_INDEX_3.ibd
1630212 FTS_000000000000224a_00000000000036b9_INDEX_4.ibd
1978372 FTS_000000000000224a_00000000000036b9_INDEX_5.ibd
1159172 FTS_000000000000224a_00000000000036b9_INDEX_6.ibd
9437208 total
aunque el problema también se desencadena por tablas con un tamaño de datos FTS significativamente menos masivo como este:
# du -c FTS_0000000000002467_0000000000003a21_INDEX*
49156 FTS_0000000000002467_0000000000003a21_INDEX_1.ibd
225284 FTS_0000000000002467_0000000000003a21_INDEX_2.ibd
147460 FTS_0000000000002467_0000000000003a21_INDEX_3.ibd
135172 FTS_0000000000002467_0000000000003a21_INDEX_4.ibd
155652 FTS_0000000000002467_0000000000003a21_INDEX_5.ibd
106500 FTS_0000000000002467_0000000000003a21_INDEX_6.ibd
819224 total
El tiempo de la parada en esos casos también es aproximadamente el mismo. He abierto un error en bugs.mysql.com para que los desarrolladores puedan investigar esto.
La naturaleza de los puestos primero me hizo sospechar que la actividad de enjuague de registros era la culpable y este artículo de Percona sobre problemas de rendimiento de enjuague de registros con MySQL 5.5 describe síntomas muy similares, pero otros sucesos han demostrado que las operaciones INSERT en la única tabla MyISAM en esta base de datos también se ven afectados por el bloqueo, por lo que esto no parece ser un problema exclusivo de InnoDB.
No obstante, decidí rastrear los valores de Log sequence number
y Pages flushed up to
desde las salidas de la sección "LOG" de SHOW ENGINE INNODB STATUS
cada 10 segundos. De hecho, parece que la actividad de lavado está en curso durante la parada, ya que la distribución entre los dos valores está disminuyendo:
Mon Sep 1 14:17:08 CEST 2014 LSN: 263992263703, Pages flushed: 263973405075, Difference: 18416 K
Mon Sep 1 14:17:19 CEST 2014 LSN: 263992826715, Pages flushed: 263973811282, Difference: 18569 K
Mon Sep 1 14:17:29 CEST 2014 LSN: 263993160647, Pages flushed: 263974544320, Difference: 18180 K
Mon Sep 1 14:17:39 CEST 2014 LSN: 263993539171, Pages flushed: 263974784191, Difference: 18315 K
Mon Sep 1 14:17:49 CEST 2014 LSN: 263993785507, Pages flushed: 263975990474, Difference: 17377 K
Mon Sep 1 14:17:59 CEST 2014 LSN: 263994298172, Pages flushed: 263976855227, Difference: 17034 K
Mon Sep 1 14:18:09 CEST 2014 LSN: 263994670794, Pages flushed: 263978062309, Difference: 16219 K
Mon Sep 1 14:18:19 CEST 2014 LSN: 263995014722, Pages flushed: 263983319652, Difference: 11420 K
Mon Sep 1 14:18:30 CEST 2014 LSN: 263995404674, Pages flushed: 263986138726, Difference: 9048 K
Mon Sep 1 14:18:40 CEST 2014 LSN: 263995718244, Pages flushed: 263988558036, Difference: 6992 K
Mon Sep 1 14:18:50 CEST 2014 LSN: 263996129424, Pages flushed: 263988808179, Difference: 7149 K
Mon Sep 1 14:19:00 CEST 2014 LSN: 263996517064, Pages flushed: 263992009344, Difference: 4402 K
Mon Sep 1 14:19:11 CEST 2014 LSN: 263996979188, Pages flushed: 263993364509, Difference: 3529 K
Mon Sep 1 14:19:21 CEST 2014 LSN: 263998880477, Pages flushed: 263993558842, Difference: 5196 K
Mon Sep 1 14:19:31 CEST 2014 LSN: 264001013381, Pages flushed: 263993568285, Difference: 7270 K
Mon Sep 1 14:19:41 CEST 2014 LSN: 264001933489, Pages flushed: 263993578961, Difference: 8158 K
Mon Sep 1 14:19:51 CEST 2014 LSN: 264004225438, Pages flushed: 263993585459, Difference: 10390 K
Y a las 14:19:11 la propagación ha alcanzado su mínimo, por lo que la actividad de lavado parece haber cesado aquí, solo coincidiendo con el final del puesto. Pero estos puntos me hicieron descartar el enrojecimiento del registro de InnoDB como la causa:
- para que la operación de lavado bloquee todas las actualizaciones de la base de datos, debe ser "sincrónica", lo que significa que 7/8 del espacio de registro debe estar ocupado
- Estaría precedido por una fase de descarga "asíncrona" que comienza en el
innodb_max_dirty_pages_pct
nivel de llenado, que no veo - los LSN siguen aumentando incluso durante la parada, por lo que la actividad de registro no cesa por completo
- Las inserciones de la tabla MyISAM también se ven afectadas
- el subproceso page_cleaner para el enjuague adaptativo parece hacer su trabajo y enjuagar los registros sin hacer que se detengan las consultas DML:
(los números son ([Log Sequence Number] - [Pages flushed up to]) / 1024
de SHOW ENGINE INNODB STATUS
)
El problema parece algo aliviado mediante la configuración innodb_adaptive_flushing_lwm=1
, lo que obliga al limpiador de páginas a hacer más trabajo que antes.
El error.log
no tiene entradas que coincidan con los puestos. SHOW INNODB STATUS
Los extractos después de aproximadamente 24 horas de operación se ven así:
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 789330
OS WAIT ARRAY INFO: signal count 1424848
Mutex spin waits 269678, rounds 3114657, OS waits 65965
RW-shared spins 941620, rounds 20437223, OS waits 442474
RW-excl spins 451007, rounds 13254440, OS waits 215151
Spin rounds per wait: 11.55 mutex, 21.70 RW-shared, 29.39 RW-excl
------------------------
LATEST DETECTED DEADLOCK
------------------------
2014-09-03 10:33:55 7fe0e2e44700
[...]
--------
FILE I/O
--------
[...]
932635 OS file reads, 2117126 OS file writes, 1193633 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 17.00 writes/s, 1.20 fsyncs/s
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Main thread process no. 54745, id 140604272338688, state: sleeping
Number of rows inserted 528904, updated 1596758, deleted 99860, read 3325217158
5.40 inserts/s, 10.40 updates/s, 0.00 deletes/s, 122969.21 reads/s
Entonces, sí, la base de datos tiene puntos muertos, pero son muy poco frecuentes (el "último" se manejó alrededor de 11 horas antes de que se hayan leído las estadísticas).
Intenté rastrear los valores de la sección "SEMÁFOROS" durante un período de tiempo, especialmente en una situación de operación normal y durante una parada (escribí un pequeño script que verificaba la lista de procesos del servidor MySQL y ejecutaba un par de comandos de diagnóstico en una salida de registro en caso de un puesto obvio). Como los números se han tomado en diferentes períodos de tiempo, he normalizado los resultados a eventos / segundo:
normal stall
1h avg 1m avg
OS WAIT ARRAY INFO:
reservation count 5,74 1,00
signal count 24,43 3,17
Mutex spin waits 1,32 5,67
rounds 8,33 25,85
OS waits 0,16 0,43
RW-shared spins 9,52 0,76
rounds 140,73 13,39
OS waits 2,60 0,27
RW-excl spins 6,36 1,08
rounds 178,42 16,51
OS waits 2,38 0,20
No estoy muy seguro de lo que estoy viendo aquí. La mayoría de los números han disminuido en un orden de magnitud, probablemente debido a las operaciones de actualización cesadas, "Mutex spin waits" y "Mutex spin rounds" sin embargo, ambos han aumentado en un factor de 4.
Investigando esto más a fondo, la lista de mutexes ( SHOW ENGINE INNODB MUTEX
) tiene ~ 480 entradas mutex listadas tanto en operación normal como durante una parada. He habilitado innodb_status_output_locks
para ver si me va a dar más detalles.
Variables de configuracion
(He jugado con la mayoría de ellos sin éxito definitivo):
mysql> show global variables where variable_name like 'innodb_adaptive_flush%';
+------------------------------+-------+
| Variable_name | Value |
+------------------------------+-------+
| innodb_adaptive_flushing | ON |
| innodb_adaptive_flushing_lwm | 1 |
+------------------------------+-------+
mysql> show global variables where variable_name like 'innodb_max_dirty_pages_pct%';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_max_dirty_pages_pct | 50 |
| innodb_max_dirty_pages_pct_lwm | 10 |
+--------------------------------+-------+
mysql> show global variables where variable_name like 'innodb_log_%';
+-----------------------------+-----------+
| Variable_name | Value |
+-----------------------------+-----------+
| innodb_log_buffer_size | 8388608 |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 268435456 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
+-----------------------------+-----------+
mysql> show global variables where variable_name like 'innodb_double%';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| innodb_doublewrite | ON |
+--------------------+-------+
mysql> show global variables where variable_name like 'innodb_buffer_pool%';
+-------------------------------------+----------------+
| Variable_name | Value |
+-------------------------------------+----------------+
| innodb_buffer_pool_dump_at_shutdown | OFF |
| innodb_buffer_pool_dump_now | OFF |
| innodb_buffer_pool_filename | ib_buffer_pool |
| innodb_buffer_pool_instances | 8 |
| innodb_buffer_pool_load_abort | OFF |
| innodb_buffer_pool_load_at_startup | OFF |
| innodb_buffer_pool_load_now | OFF |
| innodb_buffer_pool_size | 29360128000 |
+-------------------------------------+----------------+
mysql> show global variables where variable_name like 'innodb_io_capacity%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| innodb_io_capacity | 200 |
| innodb_io_capacity_max | 2000 |
+------------------------+-------+
mysql> show global variables where variable_name like 'innodb_lru_scan_depth%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_lru_scan_depth | 1024 |
+-----------------------+-------+
Las cosas ya lo intentaron
- deshabilitar el caché de consultas por
SET GLOBAL query_cache_size=0
- aumentando
innodb_log_buffer_size
a 128M - jugando con
innodb_adaptive_flushing
,innodb_max_dirty_pages_pct
y los respectivos_lwm
valores (que fueron configurados con los valores antes de mis cambios) - creciente
innodb_io_capacity
(2000) yinnodb_io_capacity_max
(4000) - ajuste
innodb_flush_log_at_trx_commit = 2
- ejecutándose con innodb_flush_method = O_DIRECT (sí, usamos una SAN con un caché de escritura persistente)
- configurando / sys / block / sda / queue / Scheduler en
noop
odeadline