PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Auswahlabfrage mit Offset-Limit ist zu langsam

Es ist langsam, weil es den obersten offset finden muss Zeilen und scannen Sie die nächsten 100. Daran ändert auch keine noch so große Optimierung etwas, wenn Sie es mit großen Offsets zu tun haben.

Dies liegt daran, dass Ihre Abfrage buchstäblich anweist die DB-Engine, viele Zeilen zu besuchen, indem sie offset 3900000 verwendet -- das sind 3,9 Millionen Zeilen. Es gibt nicht viele Möglichkeiten, dies etwas zu beschleunigen.

Superschneller RAM, SSDs etc. helfen dabei. Aber Sie gewinnen dabei nur um einen konstanten Faktor, was bedeutet, dass Sie die Dose nur die Straße hinunter treten, bis Sie einen ausreichend großen Offset erreichen.

Sicherzustellen, dass die Tabelle in den Speicher passt und viel mehr übrig ist, hilft ebenfalls durch einen größeren konstanten Faktor – außer beim ersten Mal. Dies ist jedoch mit einer ausreichend großen Tabelle oder einem ausreichend großen Index möglicherweise nicht möglich.

Sicherzustellen, dass Sie nur Index-Scans durchführen, wird bis zu einem gewissen Grad funktionieren. (Siehe die Antwort von velis; sie hat viele Vorteile.) Das Problem hier ist, dass Sie sich einen Index praktisch als eine Tabelle vorstellen können, die einen Speicherort auf der Festplatte und die indizierten Felder speichert. (Es ist optimierter als das, aber es ist eine vernünftige erste Annäherung.) Bei genügend Zeilen werden Sie immer noch auf Probleme mit einem ausreichend großen Offset stoßen.

Der Versuch, die genaue Position der Zeilen zu speichern und beizubehalten, ist ebenfalls ein kostspieliger Ansatz. (Dies wird z. B. von benjist vorgeschlagen.) Obwohl technisch machbar, leidet es unter ähnlichen Einschränkungen wie denen, die sich aus der Verwendung von MPTT mit einer Baumstruktur ergeben:Sie werden bei Lesevorgängen erheblich gewinnen, aber am Ende übermäßig lange Schreibzeiten haben, wenn ein Knoten so eingefügt, aktualisiert oder entfernt wird, dass große Teile der Daten nebenher aktualisiert werden müssen.

Wie hoffentlich deutlicher wird, gibt es kein echtes Wundermittel, wenn Sie mit so großen Offsets zu tun haben. Es ist oft besser, nach alternativen Ansätzen zu suchen.

Wenn Sie basierend auf der ID (oder einem Datumsfeld oder einem anderen indexierbaren Satz von Feldern) paginieren, besteht ein möglicher Trick (der beispielsweise von blogspot verwendet wird) darin, Ihre Abfrage an einem beliebigen Punkt im Index beginnen zu lassen.

Anders ausgedrückt, statt:

example.com?page_number=[huge]

Mach so etwas wie:

example.com?page_following=[huge]

Auf diese Weise behalten Sie den Überblick darüber, wo Sie sich in Ihrem Index befinden, und die Abfrage wird sehr schnell, da sie direkt zum richtigen Startpunkt gehen kann, ohne durch eine Unmenge von Zeilen zu pflügen:

select * from foo where ID > [huge] order by ID limit 100

Natürlich verlieren Sie die Fähigkeit, z. Seite 3000. Aber denken Sie ehrlich darüber nach:Wann sind Sie das letzte Mal zu einer riesigen Seitenzahl auf einer Website gesprungen, anstatt direkt zum Monatsarchiv zu gehen oder das Suchfeld zu verwenden?

Wenn Sie paginieren, aber den Seitenversatz auf jeden Fall beibehalten möchten, besteht ein weiterer Ansatz darin, die Verwendung größerer Seitenzahlen zu verbieten. Es ist nicht albern:Es ist das, was Google mit den Suchergebnissen macht. Wenn Sie eine Suchanfrage ausführen, gibt Google Ihnen eine geschätzte Anzahl von Ergebnissen (Sie können eine angemessene Zahl mit explain erhalten ) und ermöglicht es Ihnen dann, die ersten paar tausend Ergebnisse zu durchsuchen – mehr nicht. Sie tun dies unter anderem aus Leistungsgründen – genau dem, auf den Sie gerade stoßen.