web-dev-qa-db-fra.com

Pourquoi le décalage MYSQL supérieur LIMIT ralentit-il la requête?

Scénario en bref: une table avec plus de 16 millions d'enregistrements [taille de 2 Go]. Plus l'offset LIMIT est élevé avec SELECT, plus la requête ralentit lorsque vous utilisez ORDER BY * primary_key *

Alors

SELECT * FROM large ORDER BY `id`  LIMIT 0, 30 

prend beaucoup moins que

SELECT * FROM large ORDER BY `id` LIMIT 10000, 30 

Cela ne commande que 30 disques et le même de toute façon. Donc, ce ne sont pas les frais généraux de ORDER BY.
Maintenant, pour récupérer les 30 dernières lignes, cela prend environ 180 secondes. Comment puis-je optimiser cette requête simple?

151
Rahman

Il est normal que des décalages élevés ralentissent la requête, car celle-ci doit compter le premier OFFSET + LIMIT enregistre (et n'en prend que LIMIT). Plus cette valeur est élevée, plus la requête est longue.

La requête ne peut pas aller directement à OFFSET parce que, d’une part, les enregistrements peuvent avoir une longueur différente, puis, qu’il peut y avoir des lacunes dans les enregistrements supprimés. Il doit vérifier et compter chaque enregistrement en chemin.

En supposant que id soit un PRIMARY KEY d'une table MyISAM, vous pouvez l'accélérer en utilisant cette astuce:

SELECT  t.*
FROM    (
        SELECT  id
        FROM    mytable
        ORDER BY
                id
        LIMIT 10000, 30
        ) q
JOIN    mytable t
ON      t.id = q.id

Voir cet article:

174
Quassnoi

J'ai eu exactement le même problème moi-même. Étant donné que vous souhaitez collecter une grande quantité de ces données et non un ensemble spécifique de 30, vous exécuterez probablement une boucle et incrémenterez le décalage de 30.

Donc, ce que vous pouvez faire à la place est:

  1. Conservez le dernier identifiant d'un ensemble de données (30) (par exemple lastId = 530)
  2. Ajouter la condition WHERE id > lastId limit 0,30

Ainsi, vous pouvez toujours avoir un décalage ZÉRO. Vous serez surpris par l'amélioration des performances.

190
Nikos Kyr

MySQL ne peut pas aller directement au 10000ème enregistrement (ni au 80000ème octet, selon votre suggestion) car il ne peut pas supposer qu'il est emballé/ordonné de la sorte (ou qu'il a des valeurs continues comprises entre 1 et 10 000). Bien que cela puisse être le cas, MySQL ne peut pas supposer qu’il n’ya pas de trous/lacunes/ids supprimés.

Donc, comme noté dans les bobs, MySQL devra récupérer 10 000 lignes (ou parcourir les 10000e entrées de l'index sur id) avant de trouver les 30 à retourner.

EDIT : Pour illustrer mon propos

Notez que bien

SELECT * FROM large ORDER BY id LIMIT 10000, 30 

serait lent (er) ,

SELECT * FROM large WHERE id >  10000 ORDER BY id LIMIT 30 

serait rapide (er) et renverrait les mêmes résultats à condition qu'il ne manque pas de ids (c'est-à-dire des espaces vides).

17
Riedsio

J'ai trouvé un exemple intéressant d'optimiser les requêtes SELECT ORDER BY id LIMIT X, Y. J'ai 35 millions de lignes, il a donc fallu environ 2 minutes pour trouver une plage de lignes.

Voici le truc:

select id, name, address, phone
FROM customers
WHERE id > 990
ORDER BY id LIMIT 1000;

Il suffit de mettre le WHERE avec le dernier identifiant que vous avez obtenu pour augmenter considérablement les performances. Pour moi, c'était de 2 minutes à 1 seconde :)

Autres astuces intéressantes ici: http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/

Ça marche aussi avec des cordes

7
sym

La partie fastidieuse des deux requêtes consiste à extraire les lignes de la table. Logiquement, dans le LIMIT 0, 30 version, seules 30 lignes doivent être récupérées. Dans le LIMIT 10000, 30 version, 10000 lignes sont évaluées et 30 lignes sont renvoyées. Il peut y avoir une optimisation possible dans le processus de lecture des données, mais considérons les points suivants:

Et si vous aviez une clause WHERE dans les requêtes? Le moteur doit renvoyer toutes les lignes qualifiées, puis trier les données et obtenir les 30 lignes.

Considérez également le cas où les lignes ne sont pas traitées dans la séquence ORDER BY. Toutes les lignes qualifiantes doivent être triées pour déterminer les lignes à renvoyer.

5
bobs