sql >> Base de Datos >  >> RDS >> Mysql

La consulta de agregación AVG () muy simple en el servidor MySQL lleva mucho tiempo

Para contar el número de filas con una fecha específica, MySQL tiene que ubicar ese valor en el índice (que es bastante rápido, después de todo para eso están hechos los índices) y luego leer las entradas subsiguientes del índice hasta que encuentre la siguiente fecha. Dependiendo del tipo de datos de esi , esto se sumará a la lectura de algunos MB de datos para contar sus 700k filas. Leer algunos MB no lleva mucho tiempo (y es posible que los datos ya estén almacenados en caché en el grupo de búfer, dependiendo de la frecuencia con la que use el índice).

Para calcular el promedio de una columna que no está incluida en el índice, MySQL, nuevamente, usará el índice para encontrar todas las filas para esa fecha (igual que antes). Pero además, por cada fila que encuentra, tiene que leer los datos reales de la tabla para esa fila, lo que significa usar la clave principal para ubicar la fila, leer algunos bytes y repetir esto 700k veces. Este "acceso aleatorio" es mucho más lento que la lectura secuencial en el primer caso. (Esto empeora con el problema de que "algunos bytes" son innodb_page_size (16 KB de forma predeterminada), por lo que es posible que tenga que leer hasta 700 000 * 16 KB =11 GB, en comparación con "algunos MB" para count(*); y dependiendo de la configuración de su memoria, es posible que algunos de estos datos no se almacenen en caché y deban leerse desde el disco).

Una solución a esto es incluir todas las columnas utilizadas en el índice (un "índice de cobertura"), p. crear un índice en date, 01 . Entonces MySQL no necesita acceder a la tabla en sí, y puede proceder, de manera similar al primer método, simplemente leyendo el índice. El tamaño del índice aumentará un poco, por lo que MySQL necesitará leer "algunos MB más" (y realizar el avg -operación), pero debería ser cuestión de segundos.

En los comentarios, mencionó que necesita calcular el promedio en 24 columnas. Si desea calcular el avg para varias columnas al mismo tiempo, necesitaría un índice de cobertura en todas ellas, p. date, 01, 02, ..., 24 para evitar el acceso a la mesa. Tenga en cuenta que un índice que contiene todas las columnas requiere tanto espacio de almacenamiento como la propia tabla (y llevará mucho tiempo crear dicho índice), por lo que podría depender de la importancia de esta consulta si valen esos recursos.

Para evitar el límite de MySQL de 16 columnas por índice , puede dividirlo en dos índices (y dos consultas). Crear, p. los índices date, 01, .., 12 y date, 13, .., 24 , luego usa

select * from (select `date`, avg(`01`), ..., avg(`12`) 
               from mytable where `date` = ...) as part1
cross join    (select avg(`13`), ..., avg(`24`) 
               from mytable where `date` = ...) as part2;

Asegúrate de documentarlo bien, ya que no hay una razón obvia para escribir la consulta de esta manera, pero podría valer la pena.

Si solo promedia una sola columna, podría agregar 24 índices separados (en date, 01 , date, 02 , ...), aunque en total requerirán aún más espacio, pero pueden ser un poco más rápidos (ya que son más pequeños individualmente). Pero el grupo de búfer aún puede favorecer el índice completo, dependiendo de factores como los patrones de uso y la configuración de la memoria, por lo que es posible que deba probarlo.

Desde date es parte de su clave principal, también podría considerar cambiar la clave principal a date, esi . Si encuentra las fechas por la clave principal, no necesitaría un paso adicional para acceder a los datos de la tabla (ya que ya accede a la tabla), por lo que el comportamiento sería similar al índice de cobertura. Pero este es un cambio significativo en su tabla y puede afectar todas las demás consultas (que, por ejemplo, usan esi para ubicar las filas), por lo que debe considerarse cuidadosamente.

Como mencionó, otra opción sería crear una tabla de resumen donde almacene valores precalculados, especialmente si no agrega o modifica filas para fechas pasadas (o puede mantenerlas actualizadas con un disparador).