Esto es lo que está pasando.
The SELECT COUNT (...) icd_index where icd='25000'
utilizará el índice, que es un BTree separado de los datos. Pero lo escanea de esta manera:
- Encuentre la primera entrada que tenga icd='25000'. Esto es casi instantáneo.
- Explore hacia adelante hasta que encuentre un cambio en icd. Esto escaneará solo el índice, sin tocar los datos. Según EXPLAIN, habrá alrededor de 910 104 entradas de índice para escanear.
Ahora veamos el BTree para ese índice. Según los campos del índice, cada fila tendrá exactamente 22 bytes, además de que habrá algunos gastos generales (estimación del 40 %). Un bloque de índice MyISAM es de 1 KB (compárese con los 16 KB de InnoDB). Estimaría 33 filas por bloque. 910,104/33 dice que se deben leer alrededor de 27K bloques para hacer el CONTEO. (Nota COUNT(core_id)
necesita verificar core_id
por ser nulo, COUNT(*)
no es; esta es una diferencia menor.) La lectura de 27 000 bloques en un disco duro normal tarda unos 270 segundos. Tuviste suerte de hacerlo en 60 segundos.
La segunda ejecución encontró todos esos bloques en key_buffer (suponiendo que key_buffer_size es de al menos 27 MB), por lo que no tuvo que esperar por el disco. Por lo tanto, fue mucho más rápido. (Esto ignora el caché de consultas, que tuvo la sabiduría de vaciar o usar SQL_NO_CACHE).
5.6 resulta ser irrelevante (pero gracias por mencionarlo), ya que este proceso no ha cambiado desde 4.0 o antes (excepto que utf8 no existía; más sobre eso a continuación).
Cambiar a InnoDB ayudaría de varias maneras. La CLAVE PRIMARIA estaría 'agrupada' con los datos, no almacenada como un BTree separado. Por lo tanto, una vez que los datos o el PK se almacenan en caché, el otro está disponible de inmediato. La cantidad de bloques sería más como 5K, pero serían bloques de 16KB. Estos pueden cargarse más rápido si el caché está frío.
Usted pregunta "¿Necesito un índice solo en icd?". Bueno, eso reduciría el tamaño de MyISAM BTree a aproximadamente 21 bytes por fila, por lo que el BTree tendría aproximadamente 21/27 del tamaño, no hay mucha mejora (al menos para el situación de caché en frío).
Otro pensamiento es, si icd
siempre es numérico y siempre numérico, para usar MEDIUMINT UNSIGNED
y agregue ZEROFILL
si puede tener ceros a la izquierda.
Vaya, no me di cuenta del CONJUNTO DE CARACTERES. (He corregido los números anteriores, pero déjame explicarlo).
- CHAR(5) permite 5 caracteres .
- ascii ocupa 1 byte por carácter .
- utf8 ocupa hasta 3 bytes por caracteres .
- Entonces, CHAR(5) CHARACTER SET utf8 ocupa 15 bytes siempre .
Cambiando la columna a CHAR(5) CHARACTER SET ascii
lo reduciría a 5 bytes.
Cambiarlo a MEDIUMINT UNSIGNED ZEROFILL lo reduciría a 3 bytes.
Reducir los datos aceleraría la E/S en una cantidad aproximadamente proporcional (después de permitir otros 6 bytes para los otros dos campos).