web-dev-qa-db-fra.com

L'utilisation de LIMIT améliore-t-elle les performances et est-elle perceptible?

Je veux comprendre ce qui suit.
Supposons que j'ai une requête compliquée avec disons une jointure de 5 tables par groupe par sommations et par ordre.
Laisser de côté toute optimisation de la requête elle-même, par exemple index, etc.
Y a-t-il un avantage significatif en termes de performances en utilisant LIMIT? Je suppose que toutes les requêtes (et les résultats) doivent être traités avant LIMIT est appliqué, donc l'utilisation d'un LIMIT pour récupérer un sous-ensemble des résultats, cela offre-t-il une amélioration significative/perceptible?

11
Jim

Si vous souhaitez profiter de LIMIT pour améliorer les performances, vous devez

  • comprendre les données que vous récupérez
  • indexation correcte de la bonne séquence de colonnes
  • prendre la responsabilité de refactoriser la requête
  • en utilisant LIMIT avant JOIN

Ces principes peuvent aller très loin si vous pouvez les orchestrer.

J'ai appris ces concepts en en regardant cette vidéo YouTube (écoutez attentivement l'accent français)

J'ai utilisé ces concepts pour répondre à une question StackOverflow très difficile sur l'obtention des 40 meilleurs articles de certaines tables: 12 mai 2011: Récupération d'une seule ligne à partir de la table de jointure .

Dans ma réponse à cette question (16 mai 2011) , j'ai écrit la requête suivante et l'ai testée à fond:

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;

Veuillez noter la ligne dans la requête avec le LIMIT

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

Cette sous-requête est enfouie à trois niveaux de profondeur. Cela m'a permis d'obtenir les 40 derniers articles en utilisant LIMIT. Ensuite, j'ai effectué les JOIN nécessaires par la suite.

LEÇONS APPRISES

  • Faire LIMIT à l'intérieur des sous-requêtes peut ne pas toujours être la réponse en raison de la cardinalité des index, du contenu des données et de la taille du jeu de résultats à partir de LIMIT. Si vous avez tous vos "canards d'affilée" (ayez les quatre principes à l'esprit pour votre requête), vous pouvez obtenir des résultats étonnamment bons.
  • Rendez vos requêtes aussi simplistes que possible lorsque vous effectuez LIMIT en rassemblant uniquement les clés.
10
RolandoMySQLDBA

Lorsqu'une requête est exécutée, elle est d'abord traduite en un plan composé de plusieurs opérateurs. Il existe deux types d'opérateurs de base: bloquant et non bloquant. Un opérateur non bloquant récupère une ligne (ou quelques lignes) de son ou ses enfants pour chaque ligne demandée. Un opérateur de blocage, d'autre part, doit lire et traiter l'ensemble de lignes de tous ses enfants avant de pouvoir produire une sortie.

Le tri est un opérateur de blocage typique. Ainsi, une sélection avec commande par ne bénéficie pas beaucoup d'une limite. Cependant, il existe des SGBDR qui peuvent utiliser un algorithme de tri qui nécessite moins de mémoire et est plus rapide lorsqu'une clause limite est fournie. Dans ce cas, il suffit de stocker les n premières lignes actuellement et de les retirer de la mémoire au fur et à mesure que les lignes précédentes se présentent. Cela peut être un gain de performances significatif. Cependant, je ne suis pas sûr à 100% que MySQL ait cette capacité.

Quoi qu'il en soit, même un tri par limite doit toujours traiter l'ensemble de lignes d'entrée avant de pouvoir produire la première ligne de sortie. Bien que cet algorithme, s'il est implémenté, puisse accélérer le tri, si le reste de la requête est la partie la plus coûteuse, le temps d'exécution total ne s'améliorera pas de manière significative en raison d'une limite fournie.

2
Sebastian Meine

Dans mon cas, je peux dire Oui, même si je ne comprends toujours pas pourquoi.

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**)

Notez le temps: 18 secondes. Même demande avec une grosse LIMITE:

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**)

Plus de dix fois plus rapide !!!

EXPLAIN donne le même résultat pour les deux requêtes.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| 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 ne devrait interférer que pour limiter le jeu de résultats (c'est-à-dire que si je fais un LIMIT 4, je n'ai que les 4 premières lignes du jeu de résultats ci-dessus).

0