Problemas de rendimiento en MySql en queries con ORDER BY y LIMIT

6

MySQL adolece de un pequeño problema de optimización en queries que incluyen ORDER BY y LIMIT, provocando que el tiempo de ejecución de estas consultas se dispare de forma alarmante. Esto sucede al menos hasta la versión 5.1.47, que es la última disponible de forma oficial en los servidores de Amazon a día de hoy.

Este problemas nos lo podemos encontrar fácilmente en casos en los que estemos paginando resultados.

Para ver más claramente lo que sucede y como solucionarlo os pongo un sencillo ejemplo.

Imaginemos que tenemos un tabla llamada «t_mstr_tracking» que contiene más de 400.000 registros (la tabla tiene índices en los campos de búsqueda y orden correctamente creados), en la que vamos a realizar 3 consultas, añadiendo distintos pasos:

  • – primero una consulta que busque registros cuyo campo «tracking_visit» sea cero
  • – añadiremos un ORDER BY por un campo TIMESTAMP
  • – por último incorporamos un LIMIT 1 para obtener solo el primer resultado
SELECT * FROM t_mstr_tracking WHERE tracking_visit=0;
/* 0 rows affected, 1.177 rows found. Duration for 1 query: 0,250 sec. (+ 0,421 sec. network) */

SELECT * FROM t_mstr_tracking WHERE tracking_visit=0 ORDER BY tracking_dateopen;
/* 0 rows affected, 1.177 rows found. Duration for 1 query: 0,281 sec. (+ 0,405 sec. network) */

SELECT * FROM t_mstr_tracking WHERE tracking_visit=0 ORDER BY tracking_dateopen LIMIT 1;
/* 0 rows affected, 1 rows found. Duration for 1 query: 44,149 sec. */

Como podemos ver las dos primeras consultas se ejecutan realmente rápido, pero a la que añadimos el LIMIT en la tercera, el rendimiento baja de forma abismal: ¡de 0,25 a 44 segundos!

Para solucionar este problema, será tan sencillo como descomponer la consulta en dos (no tiene mucho sentido, pero funciona):

SELECT * FROM ( SELECT * FROM t_mstr_tracking WHERE tracking_visit=0 ORDER BY tracking_dateopen ) AS t LIMIT 1;
/* 0 rows affected, 1 rows found. Duration for 1 query: 0,640 sec. */

De esta manera hemos conseguido bajar de 44 a 0,6 segundos.

He obviado poner captura de los explains de las queries ya que en este caso no ofrecen ninguna información de ayuda.

No Comments