Estoy trabajando en un esquema para un sistema de análisis que rastrea los tiempos de uso, y es necesario ver el tiempo de uso total en un cierto rango de fechas.
Para dar un ejemplo simple, este tipo de consulta se ejecutará con frecuencia:
select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
Esta consulta generalmente toma alrededor de 7 segundos en una tabla que está muy poblada. Tiene ~ 35 millones de filas, MyISAM en MySQL ejecutándose en Amazon RDS (db.m3.xlarge).
Deshacerse de la cláusula WHERE hace que la consulta tome solo 4 segundos, y agregar una segunda cláusula (time_off> XXX) agrega 1.5 segundos adicionales, lo que lleva el tiempo de consulta a 8.5 segundos.
Como sé que este tipo de consultas se realizarán comúnmente, me gustaría optimizar las cosas para que sean más rápidas, idealmente por debajo de 5 segundos.
Comencé agregando un índice en time_on, y aunque eso aceleró drásticamente una consulta WHERE "=", no tuvo ningún efecto en la consulta ">". ¿Hay alguna manera de crear un índice que acelere las consultas WHERE ">" o "<"?
O si hay alguna otra sugerencia sobre el rendimiento de este tipo de consulta, hágamelo saber.
Nota: Estoy usando el campo "diff_ms" como un paso de desnormalización (es igual a time_off - time_on) que mejora el rendimiento de la agregación en un 30% -40%.
Estoy creando el índice con este comando:
ALTER TABLE writetest_table ADD INDEX time_on (time_on) USING BTREE;
Ejecutar "explicar" en la consulta original (con "time_on>") dice que time_on es una "posible_clave" y select_type es "SIMPLE". La columna "extra" dice "Uso de where" y "type" es "ALL". Después de agregar el índice, la tabla dice que "time_on" es el tipo de clave "MUL", lo que parece correcto ya que el mismo tiempo puede estar presente dos veces.
Aquí está el esquema de la tabla:
CREATE TABLE `writetest_table` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sessionID` int(11) DEFAULT NULL,
`time_on` timestamp NULL DEFAULT NULL,
`time_off` timestamp NULL DEFAULT NULL,
`diff_ms` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `time_on` (`time_on`)
) ENGINE=MyISAM AUTO_INCREMENT=50410902 DEFAULT CHARSET=latin1;
ACTUALIZACIÓN: ¡Creé el siguiente índice basado en la respuesta de ypercube, pero esto aumenta el tiempo de consulta para la primera consulta a alrededor de 17 segundos!
ALTER TABLE writetest_table ADD INDEX time_on__diff_ms__ix (time_on, diff_ms) ;
ACTUALIZACIÓN 2: EXPLICAR la salida
mysql> explain select sum(diff_ms) from writetest_table where time_on > '2015-07-13 15:11:56';
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| 1 | SIMPLE | writetest_table_old | index | time_on__diff_ms__ix | time_on__diff_ms__ix | 10 | NULL | 35831102 | Using where; Using index |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
1 row in set (0.00 sec)
Actualización 3: resultado de la consulta solicitada
mysql> SELECT time_on FROM writetest_table ORDER BY time_on LIMIT 1;
+---------------------+
| time_on |
+---------------------+
| 2015-07-13 15:11:56 |
+---------------------+
1 row in set (0.01 sec)
time_on
ydiff_ms
)? ¿Qué sucede si agrega la consultaWHERE ... AND diff_ms IS NOT NULL
?SELECT COUNT(*), COUNT(diff_ms) FROM writetest_table;
writetest_table_old
" mientras que la consulta tienefrom writetest_table
. ¿Es un error tipográfico o ejecuta la consulta en una tabla diferente?Respuestas:
Creo que estoy empezando a entender.
Cuando te pedí que corrieras
Dijiste que era
2015-07-13 15:11:56
lo que tienes en tuWHERE
cláusulaCuando hiciste la consulta
Realizó una exploración de tabla completa de 35.8 millones de filas.
Cuando hiciste la consulta
Realizó un escaneo de índice completo de 35.8 millones de filas.
Tiene sentido que la consulta sin la cláusula WHERE sea más rápida. Por qué ?
El escaneo de la tabla leería 35.8 millones de filas en una pasada lineal.
El EXPLICAR en la consulta con WHERE también arrojó 35.8 millones de filas. Una exploración de índice se comportaría un poco diferente. Si bien el BTREE mantiene el orden de las teclas, es horrible hacer escaneos de rango. En su caso particular, está realizando el peor escaneo de rango posible, que tendría la misma cantidad de entradas BTREE que filas en la tabla. MySQL tiene que atravesar las páginas BTREE (al menos a través de los nodos hoja) para leer los valores. Además, la
time_on
columna debe compararse a lo largo del camino en el orden dictado por el índice. Por lo tanto, los nodos BTREE que no son hojas también deben atravesarse.Por favor vea mis publicaciones en BTREEs
Aug 06, 2013
: En MySQL si la columna X tiene valores únicos, ¿cuál es la diferencia entre el índice UNIQUE y el índice B-Tree?Jun 28, 2012
: Beneficios de BTREE en MySQLSi la consulta fue hoy a medianoche
o incluso al mediodía de hoy
Debería tomar menos tiempo.
MORAL DE LA HISTORIA: No use una cláusula WHERE que haga un escaneo de rango ordenado igual al número de filas en la tabla de destino.
fuente
Para la consulta específica:
un índice en
(time_on, diff_ms)
sería la mejor opción. Entonces, si la consulta se ejecuta con la frecuencia suficiente o su eficiencia es crucial para su aplicación, agregue este índice:(No relacionado con la pregunta)
Y realmente, cambie el motor de la tabla a InnoDB. Es 2015 y el funeral de MyISAM fue hace algunos años.
(/despotricar)
fuente
ALTER TABLE writetest_table DROP INDEX time_on;
, 2) ejecutarANALYZE TABLE writetest_table;
y 3) volver a ejecutar la consulta. ¿El tiempo vuelve a 7 segundos?EXPLAIN select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
. ¿Se está utilizando el nuevo índice? Si no se está utilizando, diría que es su población clave, especialmente si su tiempo más temprano es solo hace unos días. A medida que el número de filas aumenta con días más distintos, la distribución de claves debería nivelarse y la EXPLICACIÓN debería ser mejor .