Verbessert die Verwendung von LIMIT die Leistung und macht sich dies bemerkbar?


11

Ich möchte folgendes verstehen.
Angenommen, ich habe eine komplizierte Abfrage mit einem Join von 5 Tabellen pro Gruppe nach Summierungen und Reihenfolge nach.
Abgesehen von Optimierungen an der Abfrage selbst, z. B. Indizes usw.
Gibt es einen signifikanten Leistungsvorteil bei der Verwendung LIMIT? Ich gehe davon aus, dass alle Abfragen (und Ergebnisse) verarbeitet werden müssen, bevor LIMIT angewendet wird. Wenn Sie also ein LIMIT verwenden, um eine Teilmenge der Ergebnisse abzurufen, bietet dies eine signifikante / spürbare Verbesserung?


2
Ich schlage vor, Sie lesen dies für die Fälle, die LIMITdie Effizienz verbessern: Optimieren von LIMIT-Abfragen
ypercubeᵀᴹ

Antworten:


10

Wenn Sie LIMITdie Leistung verbessern möchten , benötigen Sie

  • Verstehen Sie die Daten, die Sie abrufen
  • Richtige Indizierung der richtigen Reihenfolge der Spalten
  • Übernehmen Sie die Verantwortung für das Refactoring der Abfrage
  • Verwendung LIMITvorJOIN

Diese Prinzipien können einen langen Weg gehen, wenn Sie sie orchestrieren können.

Ich habe diese Konzepte gelernt, indem ich mir dieses YouTube-Video angesehen habe (hör dir den französischen Akzent genau an).

Ich habe diese Konzepte verwendet, um eine sehr schwierige StackOverflow-Frage zum Abrufen der 40 wichtigsten Artikel aus einigen Tabellen zu beantworten: 12. Mai 2011: Abrufen einer einzelnen Zeile aus der Join-Tabelle .

In meiner Antwort auf diese Frage (16. Mai 2011) habe ich die folgende Abfrage geschrieben und gründlich getestet:

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;

Bitte beachten Sie die Zeile in der Abfrage mit dem LIMIT

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

Diese Unterabfrage ist drei Ebenen tief vergraben. Dadurch konnte ich die letzten 40 Artikel verwenden LIMIT. Danach habe ich die notwendigen JOINs durchgeführt.

GEWONNENE ERKENNTNISSE

  • Das LIMITDurchführen von Unterabfragen ist aufgrund der Kardinalität der Indizes, des Dateninhalts und der Größe der Ergebnismenge aus dem möglicherweise nicht immer die Antwort LIMIT. Wenn Sie alle Ihre "Enten in einer Reihe" haben (beachten Sie die vier Prinzipien für Ihre Abfrage), können Sie überraschend gute Ergebnisse erzielen.
  • Machen Sie Ihre Abfragen so einfach wie möglich, LIMITindem Sie nur Schlüssel sammeln.

Also (A [LEFT] JOIN B) LIMIT 100ist gleichbedeutend mit (A LIMIT 100) [LEFT] JOIN (B LIMIT 100)? Wo [LEFT] JOINbedeutet äußere oder innere Verbindung
Jim

Es ist eher so (A LIMIT 100) [LEFT] JOIN B. Die Idee ist, LIMITdie Größe der Ergebnismenge so früh wie möglich zu bestimmen. Ich benutze auch LEFT JOINstatt INNER JOINweil LEFT JOINwird die Reihenfolge der Tasten auf der linken Seite beibehalten.
RolandoMySQLDBA

@ Jim Nein, das sind sie nicht. Manchmal sind sie wie diese: (A LEFT JOIN B) GROUP BY A.pk LIMIT 100können normalerweise umgeschrieben werden als (A LIMIT 100) LEFT JOIN B GROUP BY A.pk(kein INNER JOIN hier, mit inneren Verknüpfungen wären sie nicht gleichwertig.) Rolandos Beispiel ist genau ein solcher Fall.
Ypercubeᵀᴹ

@ypercube: Gibt es bei inneren Verknüpfungen nicht etwas zu tun, um von LIMIT zu profitieren?
Jim

Ich bezog mich auf die von Rolando skizzierte Umschreibstrategie. Eine Abfrage mit JOINs und LIMIT kann ebenfalls von Vorteil sein. Oder nicht. Es hängt davon ab, ob.
Ypercubeᵀᴹ

2

Wenn eine Abfrage ausgeführt wird, wird sie zuerst in einen Plan übersetzt, der aus mehreren Operatoren besteht. Es gibt zwei grundlegende Arten von Operatoren: Blockieren und Nichtblockieren. Ein nicht blockierender Operator ruft für jede von ihm angeforderte Zeile eine Zeile (oder einige Zeilen) von seinem Kind oder seinen Kindern ab. Ein Blockierungsoperator muss dagegen den gesamten Zeilensatz aller seiner untergeordneten Elemente einlesen und verarbeiten, bevor er eine Ausgabe erzeugen kann.

Sortieren ist ein typischer Blockierungsoperator. Eine Auswahl mit Bestellung von profitiert also nicht viel von einem Limit. Es gibt jedoch RDBMS, die einen Sortieralgorithmus verwenden können, der weniger Speicher benötigt und schneller ist, wenn eine Limit-Klausel bereitgestellt wird. In diesem Fall reicht es aus, nur die aktuell ersten n Zeilen zu speichern und sie aus dem Speicher zu verschieben, wenn frühere Zeilen hinzukommen. Das kann ein erheblicher Leistungsgewinn sein. Ich bin mir jedoch nicht 100% sicher, ob MySQL diese Fähigkeit besitzt.

In beiden Fällen muss auch eine Limit-Sortierung noch den gesamten Eingabezeilensatz verarbeiten, bevor die erste Ausgabezeile erstellt werden kann. Wenn dieser Algorithmus implementiert ist, kann er die Sortierung beschleunigen. Wenn der Rest der Abfrage der teuerste Teil ist, wird sich die Gesamtausführungszeit aufgrund eines vorgegebenen Grenzwerts nicht wesentlich verbessern.


Ich bin wenig verwirrt mit der Antwort. Sie erwähnen über Sortieren, aber Gruppieren nach Sortieren, nicht wahr? Wenn ich zum Beispiel die Bestellung von entfernt habe und bei der Gruppe von bleibe, gilt Ihre Antwort dann immer noch? Oder ist eine andere Analyse erforderlich?
Jim

Abhängig von der Abfrage und den vorhandenen Indizes kann GROUP BYdies möglicherweise zu einem Plan führen, der keine blockierenden Operatoren enthält.
Sebastian Meine

0

In meinem Fall kann ich ja sagen , auch wenn ich (noch) nicht verstehe warum.

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

Beachten Sie die Zeit: 18 Sekunden. Gleiche Anfrage mit einem großen Limit:

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

Mehr als zehnmal schneller !!!

EXPLAIN geben für beide Anfragen das gleiche Ergebnis.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| 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 sollte nur stören, um die Ergebnismenge zu begrenzen (dh wenn ich LIMIT 4 mache, bekomme ich nur die ersten 4 Zeilen der obigen Ergebnismenge).


erschreckend, welche Version verwenden Sie und können Sie einen vereinfachten Testfall erstellen?
Evan Carroll

1
Ihre Antwort ist kein neuer Nutzen für LIMIT. Ihre erste Abfrage wird in 18 Sekunden ausgeführt und ergibt eine Ergebnismenge. Alle Daten in der 2. Abfrage werden aufgrund der ersten Abfrage bereits im InnoDB-Pufferpool zwischengespeichert. Natürlich muss die 2. Abfrage schneller sein. Auch wenn Sie MySQL neu starten, führen Sie die 1. Abfrage aus, starten Sie MySQL neu und führen Sie die 2. Abfrage aus Abfrage erhalten Sie das gleiche Ergebnis. . Ein besseres Ergebnis für LIMITkann nur durch Folgendes erzielt werden : 1) LIMITvorher JOIN, 2) LIMIT in Sortierreihenfolge ASCoder DESC.
RolandoMySQLDBA

Vielen Dank für Ihr Interesse. Die Erstellung eines vereinfachten Testfalls kann schwierig sein.
Pierre-Olivier Vares
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.