Optimizar la consulta de agregación de MySQL
-
03-07-2019 - |
Pregunta
Tengo una tabla muy grande (~ 100 millones de registros) en MySQL que contiene información sobre archivos. Una de las piezas de información es la fecha de modificación de cada archivo.
Necesito escribir una consulta que cuente la cantidad de archivos que se ajustan a los rangos de fechas especificados. Para hacer eso, hice una pequeña tabla que especifica estos rangos (todo en días) y se ve así:
DateRanges
range_id range_name range_start range_end
1 0-90 0 90
2 91-180 91 180
3 181-365 181 365
4 366-1095 366 1095
5 1096+ 1096 999999999
Y escribió una consulta que se ve así:
SELECT r.range_name, sum(IF((DATEDIFF(CURDATE(),t.file_last_access) > r.range_start and DATEDIFF(CURDATE(),t.file_last_access) < r.range_end),1,0)) as FileCount
FROM `DateRanges` r, `HugeFileTable` t
GROUP BY r.range_name
Sin embargo, de manera bastante predecible, esta consulta tarda una eternidad en ejecutarse. Creo que es porque le estoy pidiendo a MySQL que revise la HugeFileTable 5 veces, cada vez que realice el cálculo DATEDIFF () en cada archivo.
Lo que quiero hacer en su lugar es revisar el registro HugeFileTable por registro solo una vez, y para cada archivo incremente el recuento en el total acumulado apropiado de range_name. No puedo entender cómo hacer eso ...
¿Alguien puede ayudar con esto?
Gracias.
EDITAR : Versión MySQL: 5.0.45, las tablas son MyISAM
EDIT2 : Aquí está la descripción que se solicitó en los comentarios
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE r ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
1 SIMPLE t ALL NULL NULL NULL NULL 96506321
Solución
Primero, cree un índice en HugeFileTable.file_last_access
.
Luego intente la siguiente consulta:
SELECT r.range_name, COUNT(t.file_last_access) as FileCount
FROM `DateRanges` r
JOIN `HugeFileTable` t
ON (t.file_last_access BETWEEN
CURDATE() + INTERVAL r.range_start DAY AND
CURDATE() + INTERVAL r.range_end DAY)
GROUP BY r.range_name;
Aquí está el EXPLAIN
plan que obtuve cuando probé esta consulta en MySQL 5.0.75 (editado por brevedad):
+-------+-------+------------------+----------------------------------------------+
| table | type | key | Extra |
+-------+-------+------------------+----------------------------------------------+
| t | index | file_last_access | Using index; Using temporary; Using filesort |
| r | ALL | NULL | Using where |
+-------+-------+------------------+----------------------------------------------+
Todavía no va a funcionar muy bien. Al usar GROUP BY
, la consulta incurre en una tabla temporal, que puede ser costosa. No hay mucho que puedas hacer al respecto.
Pero al menos esta consulta elimina el producto cartesiano que tenía en su consulta original.
actualización: Aquí hay otra consulta que utiliza una subconsulta correlacionada pero he eliminado la <=>.
SELECT r.range_name,
(SELECT COUNT(*)
FROM `HugeFileTable` t
WHERE t.file_last_access BETWEEN
CURDATE() - INTERVAL r.range_end DAY AND
CURDATE() - INTERVAL r.range_start DAY
) as FileCount
FROM `DateRanges` r;
El plan <=> no muestra ninguna tabla temporal o clasificación de archivos (al menos con la cantidad trivial de filas que tengo en mis tablas de prueba):
+----+--------------------+-------+-------+------------------+--------------------------+
| id | select_type | table | type | key | Extra |
+----+--------------------+-------+-------+------------------+--------------------------+
| 1 | PRIMARY | r | ALL | NULL | |
| 2 | DEPENDENT SUBQUERY | t | index | file_last_access | Using where; Using index |
+----+--------------------+-------+-------+------------------+--------------------------+
Pruebe esta consulta en su conjunto de datos y vea si funciona mejor.
Otros consejos
Bueno, comience asegurándose de que file_last_access
sea un índice para la tabla HugeFileTable
.
No estoy seguro de si esto es posible \ mejor, pero primero intente calcular los límites de fechas (archivos desde la fecha A hasta la fecha B ), luego use algunos consulta con > = y < =. Teóricamente, al menos, mejorará el rendimiento.
La comparación sería algo así como:
t.file_last_access >= StartDate AND t.file_last_access <= EndDate
Puede obtener una pequeña mejora al eliminar CURDATE () y poner una fecha en la consulta, ya que ejecutará esta función para cada fila dos veces en su SQL.