¿Usar LIMIT mejora el rendimiento y es notable?


11

Quiero entender lo siguiente.
Supongamos que tengo una consulta complicada con, digamos, una combinación de 5 tablas por grupo por suma y orden.
Dejando a un lado las optimizaciones de la consulta en sí misma, por ejemplo, índices, etc.
¿Existe algún beneficio de rendimiento significativo con el uso LIMIT? Supongo que toda la consulta (y los resultados) deben procesarse antes de que se aplique el LÍMITE, por lo que usar un LÍMITE para recuperar un subconjunto de los resultados, ¿ofrece esto alguna mejora significativa / notable?


2
Le sugiero que lea esto, para los casos que LIMITmejoran la eficiencia: Optimización de consultas LIMIT
ypercubeᵀᴹ

Respuestas:


10

Si desea aprovechar LIMITpara mejorar el rendimiento, necesita

  • Comprenda los datos que está recuperando
  • indexación adecuada de la secuencia correcta de columnas
  • asumir la responsabilidad de refactorizar la consulta
  • usando LIMITantesJOIN

Estos principios pueden recorrer un largo camino si puede orquestarlos.

Aprendí estos conceptos al ver este video de YouTube (escuche atentamente el acento francés)

Utilicé esos conceptos para responder una pregunta muy difícil de StackOverflow sobre cómo obtener los 40 artículos principales de algunas tablas: 12 de mayo de 2011: Obtener una sola fila de la tabla de unión .

En mi respuesta a esa pregunta (16 de mayo de 2011) , escribí la siguiente consulta y la probé a fondo:

SELECT
  AAA.author_id,
  AAA.date_created,
  IFNULL(BBB.title,'<NO_TITLE>') title,
  IFNULL(CCC.filename,'<NO-IMAGE>') filename,
  IFNULL(CCC.date_added,'<NO-IMAGE-DATE>') image_date
FROM
(
  SELECT
    AA.id,
    AA.date_added,
    BB.author_id,
    BB.date_created
  FROM
  (
    SELECT
      A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
      LEFT JOIN article_images B ON A.id = B.article_id
      GROUP BY A.id
  ) AA
  INNER JOIN articles BB USING (id)
) AAA
LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
LEFT JOIN article_images CCC
ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
ORDER BY AAA.date_created DESC;

Tenga en cuenta la línea en la consulta con el LIMIT

      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A

Esta subconsulta está enterrada a tres niveles de profundidad. Esto me permitió obtener los últimos 40 artículos usando LIMIT. Luego, realicé las UNIONES necesarias después.

LECCIONES APRENDIDAS

  • Hacer las LIMITsubconsultas internas puede no ser siempre la respuesta debido a la cardinalidad de los índices, el contenido de los datos y el tamaño del conjunto de resultados de LIMIT. Si tiene todos sus "patos en una fila" (tenga los cuatro principios en mente para su consulta), puede obtener resultados sorprendentemente buenos.
  • Haga sus consultas lo más simple posible cuando lo haga LIMITreuniendo solo claves.

Entonces, ¿ (A [LEFT] JOIN B) LIMIT 100es equivalente a (A LIMIT 100) [LEFT] JOIN (B LIMIT 100)? Donde [LEFT] JOINsignifica unión externa o interna
Jim

Es más como (A LIMIT 100) [LEFT] JOIN B. La idea es usar LIMITpara determinar el tamaño del conjunto de resultados lo antes posible. También uso en LEFT JOINlugar de INNER JOINporque LEFT JOINpreservará el orden de las teclas en el lado izquierdo.
RolandoMySQLDBA

@ Jim No, no lo son. A veces, como este: (A LEFT JOIN B) GROUP BY A.pk LIMIT 100generalmente se pueden reescribir como (A LIMIT 100) LEFT JOIN B GROUP BY A.pk(no hay INNER JOIN aquí, con uniones internas que no serían equivalentes). El ejemplo de Rolando es exactamente ese caso.
ypercubeᵀᴹ

@ypercube: Entonces, con las uniones internas, ¿no hay algo que hacer para beneficiarse de LIMIT?
Jim

Me refería a la estrategia de reescritura esbozada por Rolando. Una consulta con JOINs y LIMIT también puede beneficiarse. O no. Depende.
ypercubeᵀᴹ

2

Cuando se ejecuta una consulta, primero se traduce en un plan compuesto por varios operadores. Hay dos tipos básicos de operadores: Bloqueo y No bloqueo. Un operador sin bloqueo recupera una fila (o algunas filas) de su hijo o hijos para cada fila que se le solicite. Un operador de bloqueo, por otro lado, tiene que leer y procesar todo el conjunto de filas de todos sus elementos secundarios antes de que pueda producir cualquier salida.

Sort es un operador de bloqueo típico. Por lo tanto, una selección con orden por no se beneficia mucho de un límite. Sin embargo, hay RDBMS que pueden utilizar un algoritmo de clasificación que necesita menos memoria y es más rápido cuando se proporciona una cláusula límite. En este caso, es suficiente almacenar las primeras n filas actualmente y moverlas de la memoria a medida que aparecen las filas anteriores. Eso puede ser una ganancia de rendimiento significativa. Sin embargo, no estoy 100% seguro de que MySQL tenga esa capacidad.

De cualquier manera, incluso una ordenación por límite aún necesita procesar todo el conjunto de filas de entrada antes de que pueda producir la primera fila de salida. Si bien este algoritmo, si se implementa, puede acelerar la clasificación, si el resto de la consulta es la parte más costosa, el tiempo total de ejecución no mejorará significativamente debido a un límite proporcionado.


Estoy un poco confundido con la respuesta. Mencionas sobre ordenar pero agrupar por también ordena, ¿no? Entonces, si, por ejemplo, eliminé el orden y me quedo con el grupo, ¿sigue siendo válida su respuesta? ¿O se necesita un análisis diferente?
Jim

Dependiendo de la consulta y los índices presentes, GROUP BYpodría conducir a un plan que no contenga operadores de bloqueo.
Sebastian Meine

0

En mi caso, puedo decir que , incluso si (todavía) no entiendo por qué.

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id;

(result set)

8 rows in set (**18.14 sec**)

Tenga en cuenta el tiempo: 18 segundos. Misma solicitud con un LÍMITE grande:

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1 
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511 
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id
LIMIT 100000000000;

(exact same result set)

8 rows in set (**1.32 sec**)

¡Más de diez veces más rápido!

EXPLIQUE dar el mismo resultado para ambas solicitudes.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys                                     | key     | key_len | ref                          | rows   | filtered | Extra                                        |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
|  1 | SIMPLE      | a1_   | NULL       | ALL    | IDX_438010BBC10784EF                              | NULL    | NULL    | NULL                         | 795135 |    33.33 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | g0_   | NULL       | eq_ref | PRIMARY,IDX_9CA5CF6758A1D71F,IDX_9CA5CF67670C757F | PRIMARY | 4       | phs.a1_.groupe_jardinerie_id |      1 |    50.00 | Using where                                  |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+

LIMIT debe interferir solo para limitar el conjunto de resultados (es decir, si hago un LIMIT 4, solo obtengo las primeras 4 filas del conjunto de resultados anterior).


aterrador, ¿qué versión está utilizando y puede crear un caso de prueba simplificado?
Evan Carroll

1
Su respuesta no prueba ningún beneficio nuevo para LIMIT. Su primera consulta se ejecuta en 18 segundos dando un conjunto de resultados. Todos los datos en la segunda consulta ya están almacenados en caché en el grupo de búferes de InnoDB debido a la primera consulta, por lo tanto, la segunda consulta debe ser más rápida, incluso si reinicia mysql, ejecute la primera consulta, reinicie mysql y ejecute la segunda consulta, obtendrá el mismo resultado. . Tener un mejor resultado LIMITsolo puede venir de hacer: 1) LIMITantes JOIN, 2) LIMIT en orden de clasificación ASCo DESC.
RolandoMySQLDBA

Gracias por su interés. Crear un caso de prueba simplificado podría ser difícil.
Pierre-Olivier Vares
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.