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
Respuestas:
Estábamos viendo el mismo problema en dos servidores en las versiones 5.6.12 y 5.6.16 que se ejecutan en Windows, con un par de esclavos cada uno. Estuvimos perplejos, como tú, durante casi dos meses.
Solución :
Consulte https://dev.mysql.com/doc/refman/5.6/en/replication-options-binary-log.html#sysvar_binlog_order_commits para obtener detalles de la variable.
explicación :
El texto completo de InnoDB utiliza una memoria caché (de forma predeterminada, 8M de tamaño) que contiene los cambios que deben aplicarse al índice de texto completo real en el disco.
Una vez que el caché se llena, se crean un par de transacciones para realizar el trabajo de fusionar los datos contenidos en el caché; esto tiende a ser una gran cantidad de E / S aleatorias, por lo que a menos que se pueda cargar todo el índice de texto completo en el grupo de búferes, es una transacción larga y lenta.
Con binlog_order_commits establecido en verdadero, todas las transacciones que contienen inserciones y actualizaciones, iniciadas después de la transacción fts_sync_index de larga duración, deben esperar hasta que se haya completado antes de que puedan comprometerse.
Esto es solo un problema si el registro binario está habilitado
fuente
Permítanme intentar y describir el problema histórico con la descarga de registros y cómo funciona la descarga adaptativa:
Los registros de rehacer son un diseño de búfer en anillo . Solo se escriben en (nunca se leen desde el funcionamiento normal) y proporcionan recuperación en caso de bloqueo. Me gusta describir un anillo de amortiguación como similar a la banda de rodadura de un tanque.
InnoDB no podrá sobrescribir el espacio del archivo de registro si contiene cambios que aún no se han modificado en el disco. Entonces, históricamente, lo que sucedería es que InnoDB intentaría una cierta cantidad de trabajo por segundo (configurado por
innodb_io_capacity
) y si eso no fuera suficiente, alcanzaría el espacio de registro completo. Se produciría un bloqueo, ya que el enjuague síncrono debía ocurrir de repente en el espacio libre, haciendo que lo que generalmente es una tarea en segundo plano de repente se ponga en primer plano.Para abordar este problema, se introdujo el lavado adaptativo. Donde al 10% (predeterminado) de espacio de registro consumido, el trabajo de fondo comienza a ser progresivamente más agresivo. El objetivo de esto es que, en lugar de una parada repentina, tenga más de una "caída breve" en el rendimiento.
Independiente de lavado de adaptación, es importante tener suficiente espacio de registro para su carga de trabajo (
innodb_log_file_size
valores de 4G son ahora muy seguro), y asegúrese de queinnodb_io_capacity
yinnodb_lru_scan_depth
se establecen en valores realistas. El enjuague adaptativo del 10%innodb_adaptive_flushing_lwm
es algo en lo que no se estira demasiado, es más un mecanismo de defensa contra el espacio.fuente
Solo para brindarle a InnoDB algo de alivio de contención, puedes jugar
innodb_purge_threads
.Antes de MySQL 5.6, el Master Thread borraba toda la página. En MySQL 5.6, un hilo separado puede manejarlo. El valor predeterminado para
innodb_purge_threads
MySQL 5.5 era 0 con un máximo de 1. En MySQL 5.6, el valor predeterminado es 1 con un máximo de 32.¿Qué hace
innodb_purge_threads
realmente la configuración ?Comenzaría por establecer innodb_purge_threads en 4 y ver si se reduce el enjuague de la página InnoDB.
ACTUALIZACIÓN 2014-09-02 12:33 EDT
Morgan Tocker señaló en el comentario a continuación que el limpiador de páginas es la víctima y MySQL 5.7 puede abordarlo . No obstante, su situación está en MySQL 5.6.
Eché un segundo vistazo y noté que tienes innodb_max_dirty_pages_pct a los 50.
El valor predeterminado para innodb_max_dirty_pages_pct en MySQL 5.5+ es 75. Reducirlo aumentaría la incidencia de paradas por descarga. Haría tres (3) cosas
my.cnf
ACTUALIZACIÓN 2014-09-03 11:06 EDT
Es posible que deba cambiar su comportamiento de lavado
Intente configurar lo siguiente dinámicamente
Estas variables, flush y flush_time , harán que el vaciado sea más agresivo al cerrar los identificadores de archivos abiertos en las tablas cada 10 segundos. MyISAM definitivamente puede beneficiarse de él porque no almacena datos en caché. Todas las escrituras en las tablas MyISAM requieren bloqueos de tabla completos, seguidos de las escrituras atómicas, y dependen del sistema operativo para los cambios de disco.
Vaciar InnoDB de esa manera requeriría un reinicio de mysql. Las opciones para ver son innodb_flush_log_at_trx_commit e innodb_flush_method .
Antes de reiniciar, agregue estos
Antes de seguir esta ruta, debe verificar si el registro en diario es un problema. yo vi esto genial publicación de mysqlperformanceblog en O_DIRECT era falsa debido al kernel. La misma publicación también menciona que MyISAM está siendo afectado.
Escribí sobre esta publicación antes: ib_logfile se abrió con O_SYNC cuando innodb_flush_method = O_DSYNC
Darle una oportunidad !!!
fuente
sar -d
resultado es queawait
aumenta casi diez veces durante una de las paradas mientras que el rendimiento disminuye. ¿Cree que es probable que haya problemas fuera de MySQL aquí, por ejemplo con el planificador de E / S o el registro en diario del sistema de archivos?SHOW GLOBAL VARIABLES LIKE '%io_threads';