Mysql
 sql >> Datenbank >  >> RDS >> Mysql

mysql:sehr einfach SELECT id ORDER BY LIMIT wird INDEX nicht wie erwartet verwenden (?!)

Indexsuchen erfolgen nach Wert , nicht nach Position . Ein Index kann nach einem Wert 2955900 suchen, aber danach fragen Sie nicht. Sie verlangen, dass die Abfrage bei einem Offset der 2955900. Zeile in der Tabelle beginnt.

Der Optimierer kann nicht davon ausgehen, dass alle Primärschlüsselwerte aufeinander folgen. Es ist also ziemlich wahrscheinlich, dass die 2955900. Zeile einen viel höheren Wert hat.

Selbst wenn die Primärschlüsselwerte fortlaufend sind, haben Sie möglicherweise eine WHERE-Bedingung, die beispielsweise nur mit 45 % der Zeilen übereinstimmt. In diesem Fall wäre der ID-Wert in der 2955900. Zeile way über den ID-Wert 2955900 hinaus.

Mit anderen Worten, eine Indexsuche mit dem ID-Wert 2955900 liefert nicht die 2955900. Zeile.

Daher kann MySQL den Index nicht für den Offset eines Limits verwenden. Es muss Scannen Sie die Zeilen, um sie zu zählen, bis Offset+Limit-Zeilen erreicht sind.

MySQL hat Optimierungen in Bezug auf LIMIT , aber es geht mehr darum, einen Tabellenscan zu stoppen, sobald er die Anzahl der zurückzugebenden Zeilen erreicht hat. Der Optimierer kann dennoch in einem EXPLAIN-Plan melden, dass er erwartet, dass er möglicherweise muss die ganze Tabelle scannen.

Ein häufiges Missverständnis über FORCE INDEX ist, dass es die Verwendung eines Index erzwingt. :-) In der Tat, wenn die Abfrage nicht kann Verwenden Sie einen Index (oder wenn die verfügbaren Indizes keinen Nutzen für diese Abfrage haben), hat FORCE INDEX keine Wirkung.

Zu Ihrem Kommentar:

Paginierung ist ein häufiger Fluch datengesteuerter Webanwendungen. Obwohl diese Funktion sehr verbreitet ist, ist sie nicht einfach zu optimieren. Hier sind ein paar Tipps:

  • Warum fragen Sie mit Offset 2955900 ab? Erwarten Sie wirklich, dass Benutzer so viele Seiten durchsuchen? Die meisten Benutzer geben nach einigen Seiten auf (wie viele genau, hängt von der Art der Anwendung und den Daten ab).

  • Reduzieren Sie die Anzahl der Abfragen. Ihre Paginierungsfunktion könnte die ersten 5-10 Seiten abrufen, auch wenn sie dem Benutzer nur die erste Seite anzeigt. Cachen Sie die anderen Seiten in der Annahme, dass der Benutzer durch einige Seiten vorrücken wird. Nur wenn sie über den zwischengespeicherten Satz von Seiten hinausgehen, muss Ihre App eine weitere Abfrage durchführen. Sie könnten sogar alle 10 Seiten in Javascript im Browser des Clients zwischenspeichern, sodass das Klicken auf „Weiter“ sofort erfolgt für sie (zumindest für die ersten paar Seiten).

  • Platzieren Sie auf keiner Benutzeroberfläche einen „Zuletzt“-Button, da die Leute ihn aus Neugier anklicken. Beachten Sie, dass Google eine „Weiter“-Schaltfläche, aber keine „Letzte“-Schaltfläche hat. Die Benutzeroberfläche selbst hält also Benutzer davon ab, ineffiziente Abfragen mit hohen Offsets auszuführen.

  • Wenn der Benutzer jeweils um eine Seite vorrückt, verwenden Sie den höchsten ID-Wert, der auf der vorherigen Seite zurückgegeben wurde, in der WHERE-Klausel der Abfrage der nächsten Seite. Dh das Folgende tut Verwenden Sie den Index, auch ohne FORCE INDEX-Hinweis:

    SELECT * FROM thistable WHERE id > 544 LIMIT 20