Scénario en bref: une table avec plus de 16 millions d'enregistrements [2 Go de taille]. Plus le décalage LIMIT est élevé avec SELECT, plus la requête est lente lorsque vous utilisez ORDER BY * primary_key *
Alors
SELECT * FROM large ORDER BY `id` LIMIT 0, 30
prend bien moins que
SELECT * FROM large ORDER BY `id` LIMIT 10000, 30
Cela ne commande que 30 disques et de toute façon. Ce n'est donc pas la surcharge de ORDER BY.
Désormais, lors de la récupération des 30 dernières lignes, cela prend environ 180 secondes. Comment puis-je optimiser cette simple requête?
mysql
performance
sql-order-by
limit
Rahman
la source
la source
Réponses:
Il est normal que des décalages plus élevés ralentissent la requête, car la requête doit compter les premiers
OFFSET + LIMIT
enregistrements (et n'en prendre queLIMIT
). Plus cette valeur est élevée, plus la requête s'exécute longtemps.La requête ne peut pas aller directement à
OFFSET
car, premièrement, les enregistrements peuvent être de longueur différente et, deuxièmement, il peut y avoir des espaces entre les enregistrements supprimés. Il doit vérifier et compter chaque enregistrement sur son chemin.En supposant qu'il
id
s'agitPRIMARY KEY
d'uneMyISAM
table, vous pouvez l'accélérer en utilisant cette astuce:Consultez cet article:
la source
ORDER BY
ou si l'index couvre tous les champs dont vous avez besoin, vous n'avez pas besoin de cette solution de contournement.postgresql
. C'est une réponse spécifique à MySQL.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:
WHERE id > lastId limit 0,30
Ainsi, vous pouvez toujours avoir un offset ZERO. Vous serez surpris par l'amélioration des performances.
la source
MySQL ne peut pas aller directement au 10000ème enregistrement (ou au 80000ème octet comme vous le suggérez) car il ne peut pas supposer qu'il est emballé / ordonné comme ça (ou qu'il a des valeurs continues de 1 à 10000). Bien qu'il puisse en être ainsi en réalité, MySQL ne peut pas supposer qu'il n'y a pas de trous / lacunes / identifiants supprimés.
Ainsi, comme bobs l'a noté, MySQL devra récupérer 10000 lignes (ou parcourir les 10000ème entrées de l'index
id
) avant de trouver les 30 à renvoyer.EDIT : pour illustrer mon propos
Notez que bien que
serait lent (euh) ,
serait rapide (heu) , et renverrait les mêmes résultats à condition qu'il n'y ait pas de
id
s manquants (c.-à-d. lacunes).la source
J'ai trouvé un exemple intéressant pour 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 l'astuce:
Il suffit de mettre le WHERE avec le dernier identifiant 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/
Cela fonctionne aussi avec des cordes
la source
La partie chronophage des deux requêtes consiste à récupérer les lignes de la table. Logiquement parlant, dans la
LIMIT 0, 30
version, seules 30 lignes doivent être récupérées. Dans laLIMIT 10000, 30
version, 10000 lignes sont évaluées et 30 lignes sont renvoyées. Il peut y avoir une optimisation du processus de lecture des données, mais considérez ce qui suit: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 enfin 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 éligibles doivent être triées pour déterminer les lignes à renvoyer.
la source
Pour ceux qui sont intéressés par une comparaison et des chiffres :)
Expérience 1: l'ensemble de données contient environ 100 millions de lignes. Chaque ligne contient plusieurs BIGINT, TINYINT, ainsi que deux champs TEXT (délibérément) contenant environ 1k caractères.
SELECT * FROM post ORDER BY id LIMIT {offset}, 5
SELECT t.* FROM (SELECT id FROM post ORDER BY id LIMIT {offset}, 5) AS q JOIN post t ON t.id = q.id
... WHERE id>xxx LIMIT 0,5
n'apparaît pas ici car elle doit être en temps constant.Expérience 2: chose similaire, sauf qu'une ligne n'a que 3 BIGINTs.
la source