Al mirar una consulta particularmente molesta sobre tablas MyISAM que lleva mucho tiempo ejecutar en varias ocasiones, noté que MySQL parece exponer un patrón de E / S bastante extraño: al ejecutar una sola consulta y tener que hacer una tarea significativa cantidad de E / S (por ejemplo, para un escaneo de tabla o cuando las memorias caché están vacías como resultado de echo 3 > /proc/sys/vm/drop_caches
lo cual los índices deben cargarse primero del disco), el tamaño de la cola para el dispositivo de bloque subyacente está cerca del valor 1, con un rendimiento abismal de solo 4-5 MB / s:
root@mysql-test:~# iostat -xdm 5 /dev/sda
Linux 3.2.0-40-generic (mysql-test) 04/30/2014 _x86_64_ (4 CPU)
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.14 24.82 18.26 88.79 0.75 4.61 102.56 2.83 26.39 19.29 27.85 2.46 26.31
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 69.29 151.52 72.73 5.31 0.59 53.95 1.21 5.39 7.84 0.29 4.39 98.51
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 153.06 144.29 174.69 4.96 1.36 40.54 1.39 4.36 8.91 0.60 3.15 100.49
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 105.75 150.92 109.03 4.53 0.85 42.41 1.29 4.96 8.15 0.54 3.90 101.36
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 48.89 156.36 51.72 5.28 0.76 59.38 1.28 6.16 8.02 0.55 4.77 99.23
Si bien los 150 IOPS simplemente son lo que un solo disco en la configuración dada es capaz de entregar en términos de E / S aleatorias, el resultado todavía me sorprende, ya que esperaría que MySQL pueda ejecutar E / S asíncronas para lecturas y obtener un gran cantidad de bloques simultáneamente en lugar de leerlos y evaluarlos uno por uno, descuidando efectivamente las ganancias de paralelización disponibles en las configuraciones RAID. ¿Qué decisión de diseño u opción de configuración es responsable de esto? ¿Es este un problema específico de la plataforma?
Si bien he probado esto con tablas MyISAM de gran tamaño, veo efectos similares con las mismas tablas convertidas a InnoDB (aunque no es tan malo, la consulta de muestra todavía tarda 20-30 segundos y la mayor parte del tiempo se dedica a leer el disco con una longitud de cola de 1) después de reiniciar el demonio mysql y, por lo tanto, los grupos de búferes están vacíos. También verifiqué que el mismo problema persiste en 5.6 GA y el hito actual 5.7 5.7 - siempre que esté usando un solo hilo de consulta, MySQL parece incapaz de paralelizar las operaciones de E / S necesarias para el procesamiento de consultas.
Según la solicitud, algunos detalles adicionales sobre el escenario. El comportamiento se puede observar con una multitud de tipos de consulta. Elegí arbitrariamente uno para pruebas adicionales que dice algo así:
SELECT herp.id, herp.firstname, herp.lastname, derp.label, herp.email,
(SELECT CONCAT(label, " (", zip_code, " ", city,")" ) FROM subsidiaries WHERE subsidiaries.id=herp.subsidiary_id ) AS subsidiary,
(SELECT COUNT(fk_herp) from herp_missing_data WHERE fk_herp=herp.id) AS missing_data
FROM herp LEFT JOIN derp ON derp.id=herp.fk_derp
WHERE (herp.fk_pools='123456') AND herp.city LIKE '%Some City%' AND herp.active='yes'
ORDER BY herp.id desc LIMIT 0,10;
Sé que tiene algo de espacio para la optimización, pero decidí dejarlo así por varias razones y concentrarme en encontrar una explicación general del inesperado patrón de E / S que estoy viendo.
Las tablas usadas tienen un montón de datos en ellas:
mysql> select table_name, engine, table_rows, data_length, index_length from information_schema.tables WHERE tables.TABLE_SCHEMA = 'mydb' and tables.table_name in ( 'herp', 'derp', 'missing_data', 'subsidiaries');
+-------------------------+--------+------------+-------------+--------------+
| table_name | engine | table_rows | data_length | index_length |
+-------------------------+--------+------------+-------------+--------------+
| derp | MyISAM | 14085 | 1118676 | 165888 |
| herp | MyISAM | 821747 | 828106512 | 568057856 |
| missing_data | MyISAM | 1220186 | 15862418 | 29238272 |
| subsidiaries | MyISAM | 1499 | 6490308 | 103424 |
+-------------------------+--------+------------+-------------+--------------+
4 rows in set (0.00 sec)
Ahora, cuando ejecuto la consulta anterior sobre estas tablas, obtengo tiempos de ejecución de más de 1 minuto mientras el sistema aparentemente está continuamente ocupado leyendo datos del disco con un solo hilo.
El perfil para una ejecución de consulta de muestra (que tomó 1 min 9.17 segundos en este ejemplo) se ve así:
mysql> show profile for query 1;
+--------------------------------+-----------+
| Status | Duration |
+--------------------------------+-----------+
| starting | 0.000118 |
| Waiting for query cache lock | 0.000035 |
| init | 0.000033 |
| checking query cache for query | 0.000399 |
| checking permissions | 0.000077 |
| checking permissions | 0.000030 |
| checking permissions | 0.000031 |
| checking permissions | 0.000035 |
| Opening tables | 0.000158 |
| init | 0.000294 |
| System lock | 0.000056 |
| Waiting for query cache lock | 0.000032 |
| System lock | 0.000116 |
| optimizing | 0.000063 |
| statistics | 0.001964 |
| preparing | 0.000104 |
| Sorting result | 0.000033 |
| executing | 0.000030 |
| Sending data | 2.031349 |
| optimizing | 0.000054 |
| statistics | 0.000039 |
| preparing | 0.000024 |
| executing | 0.000013 |
| Sending data | 0.000044 |
| optimizing | 0.000017 |
| statistics | 0.000021 |
| preparing | 0.000019 |
| executing | 0.000013 |
| Sending data | 21.477528 |
| executing | 0.000070 |
| Sending data | 0.000075 |
| executing | 0.000027 |
| Sending data | 45.692623 |
| end | 0.000076 |
| query end | 0.000036 |
| closing tables | 0.000109 |
| freeing items | 0.000067 |
| Waiting for query cache lock | 0.000038 |
| freeing items | 0.000080 |
| Waiting for query cache lock | 0.000044 |
| freeing items | 0.000037 |
| storing result in query cache | 0.000033 |
| logging slow query | 0.000103 |
| cleaning up | 0.000073 |
+--------------------------------+-----------+
44 rows in set, 1 warning (0.00 sec)
fuente
Respuestas:
Primero déjenme aclarar confirmando que MyISAM no hace E / S asíncrona, pero que InnoDB lo hace y lo hará de manera predeterminada desde MySQL 5.5. Antes de 5.5 usaba "AIO simulado" mediante hilos de trabajo.
Creo que también es importante distinguir entre tres situaciones:
Para (1) I / O podrá ejecutarse en paralelo para esto. Existen algunos límites con MyISAM: bloqueo de tabla y un bloqueo global que protege el
key_buffer
(caché de índice). InnoDB en MySQL 5.5+ realmente brilla aquí.Para (2) esto actualmente no es compatible. Un buen caso de uso sería con la partición, donde podría buscar cada tabla particionada en paralelo.
Para (3) InnoDB tiene lectura lineal anticipada para leer en toda su extensión (grupo de 64 páginas) si se leen> 56 páginas (esto es configurable), pero hay espacio para mejoras adicionales. Facebook ha escrito sobre la implementación de cabeza de lectura lógica en su rama (con una ganancia de rendimiento de 10x en los cuadros).
fuente
Espero
missing_data
que no sea MyISAM porque una tabla MyISAM vacía generalmente tiene un byte de 1024.MYI
. Se espera un tamaño de byte distinto de cero de un MyISAM. Un byte cero me.MYI
suena un poco espeluznante.Si ejecuta esta consulta de metadatos
y el motor de esa mesa es MyISAM, debes repararlo.
NOTA LATERAL: Si
engine
es asíNULL
, es una vista. Si es una vista o no es MyISAM, ignore el resto de mi publicación y agregue esa información a la pregunta. Si la tabla es MyISAM, sigue leyendo ...Según su consulta de metadatos,
missing_data.MYD
es de aproximadamente 46M.Primero, ejecuta esto
Obtendrá la descripción de la tabla o un mensaje de error que dice algo como
Si obtiene la descripción de la tabla y es MyISAM, ejecute
Volverá a crear la tabla sin fragmentación y calculará estadísticas de índice nuevas. Si eso no funciona, intente:
Eso debería regenerar las páginas de índice para MyISAM.
Solo para estar seguro (si usa MySQL 5.6), ejecute esto después de la reparación
Tu pregunta
Es posible que los índices de su tabla no se carguen en la memoria si MySQL Query Optimizer decide no usarlo. Si su cláusula WHERE dicta que se debe leer una cantidad significativa de filas de los índices, MySQL Query Optimizer lo verá al construir el plan EXPLAIN y decidirá usar un escaneo completo de la tabla.
Las operaciones de E / S paralelas en una tabla MyISAM son inalcanzables porque no son configurables.
InnoDB se puede ajustar para aumentar el rendimiento de esa manera.
fuente