Hallo zusammen! Ich bin ein Backend-Entwickler, der Microservices in Java + Spring schreibt. Ich arbeite in einem der internen Produktentwicklungsteams bei Tinkoff.

In unserem Team stellt sich hĂ€ufig die Frage der Optimierung von Abfragen in einem DBMS. Sie möchten immer etwas schneller sein, kommen aber nicht immer mit durchdachten Indizes aus â Sie mĂŒssen nach Workarounds suchen. WĂ€hrend einer dieser StreifzĂŒge durch das Internet auf der Suche nach sinnvollen Optimierungen fĂŒr die Arbeit mit Datenbanken bin ich darauf gestoĂen , Autor von SQL Performance Explained. Dies ist die seltene Art von Blog, in der Sie alle Artikel hintereinander lesen können.
Ich wĂŒrde gerne einen kurzen Artikel von Marcus fĂŒr Sie ĂŒbersetzen. Es kann gewissermaĂen als Manifest bezeichnet werden, das auf das alte, aber immer noch relevante Problem der Leistung der Offset-Operation gemÀà dem SQL-Standard aufmerksam machen soll.
An einigen Stellen werde ich den Autor durch ErlĂ€uterungen und Kommentare ergĂ€nzen. Ich werde alle diese Orte mit âca.â bezeichnen. fĂŒr mehr Klarheit
Eine kurze EinfĂŒhrung
Ich denke, viele Leute wissen, wie problematisch und langsam die Arbeit mit der Seitenauswahl ĂŒber den Offset ist. Wussten Sie, dass es ganz einfach durch ein effizienteres Design ersetzt werden kann?
Das SchlĂŒsselwort offset weist die Datenbank also an, die ersten n DatensĂ€tze in der Anfrage zu ĂŒberspringen. Die Datenbank muss jedoch weiterhin diese ersten n DatensĂ€tze in der angegebenen Reihenfolge von der Festplatte lesen (Hinweis: Sortierung anwenden, falls angegeben), und erst dann ist es möglich, DatensĂ€tze ab n+1 zurĂŒckzugeben. Das Interessanteste ist, dass das Problem nicht in der konkreten Implementierung im DBMS liegt, sondern in der ursprĂŒnglichen Definition gemÀà Standard:
âŠdie Zeilen werden zunĂ€chst nach sortiert und dann durch Weglassen der in der angegebenen Anzahl von Zeilen begrenzt von Anfang anâŠ
-SQL:2016, Teil 2, 4.15.3 Abgeleitete Tabellen (Hinweis: derzeit der am hÀufigsten verwendete Standard)
Der entscheidende Punkt hierbei ist, dass Offset einen einzigen Parameter benötigt â die Anzahl der zu ĂŒberspringenden DatensĂ€tze, und das warâs. Nach dieser Definition kann das DBMS nur alle DatensĂ€tze abrufen und dann die unnötigen verwerfen. Offensichtlich zwingt uns diese Definition des Offsets zu zusĂ€tzlicher Arbeit. Dabei spielt es keine Rolle, ob es sich um SQL oder NoSQL handelt.
Nur ein bisschen mehr Schmerzen
Die Probleme mit dem Offset enden hier nicht, und hier erfahren Sie, warum. Was passiert in diesem Fall, wenn zwischen dem Lesen zweier Datenseiten von der Festplatte ein anderer Vorgang einen neuen Datensatz einfĂŒgt?

Wenn Offset verwendet wird, um DatensĂ€tze von vorherigen Seiten zu ĂŒberspringen, werden Sie beim HinzufĂŒgen eines neuen Datensatzes zwischen LesevorgĂ€ngen verschiedener Seiten höchstwahrscheinlich Duplikate erhalten (Hinweis: Dies ist möglich, wenn wir Seite fĂŒr Seite mit dem Order-by-Konstrukt lesen in der Mitte unserer Ausgabe könnte es einen neuen Eintrag geben).
Die Abbildung verdeutlicht diese Situation deutlich. Die Basis liest die ersten 10 DatensĂ€tze, danach wird ein neuer Datensatz eingefĂŒgt, der alle gelesenen DatensĂ€tze um 1 versetzt. Dann nimmt die Basis eine neue Seite aus den nĂ€chsten 10 DatensĂ€tzen und beginnt nicht wie vorgesehen mit dem 11., sondern mit dem 10., Duplizieren dieses Datensatzes. Es gibt noch andere Anomalien im Zusammenhang mit der Verwendung dieses Ausdrucks, aber dieser kommt am hĂ€ufigsten vor.
Wie wir bereits herausgefunden haben, handelt es sich hierbei nicht um Probleme eines bestimmten DBMS oder seiner Implementierungen. Das Problem besteht darin, die Paginierung gemÀà dem SQL-Standard zu definieren. Wir teilen dem DBMS mit, welche Seite abgerufen oder wie viele DatensĂ€tze ĂŒbersprungen werden sollen. Die Datenbank ist einfach nicht in der Lage, eine solche Anfrage zu optimieren, da hierfĂŒr zu wenig Informationen vorhanden sind.
Es sollte auch klargestellt werden, dass es sich hierbei nicht um ein Problem mit einem bestimmten SchlĂŒsselwort handelt, sondern vielmehr um die Semantik der Abfrage. Es gibt mehrere weitere Syntaxen, die in ihrer Problematik identisch sind:
- Das SchlĂŒsselwort offset ist wie bereits erwĂ€hnt.
- Eine Konstruktion aus zwei SchlĂŒsselwörtern limit [offset] (obwohl limit selbst nicht so schlecht ist).
- Filtern nach Untergrenzen, basierend auf der Zeilennummerierung (z. B. row_number(), rownum usw.).
Alle diese AusdrĂŒcke sagen Ihnen lediglich, wie viele Zeilen Sie ĂŒberspringen mĂŒssen, ohne zusĂ€tzliche Informationen oder Kontext.
SpĂ€ter in diesem Artikel wird das SchlĂŒsselwort offset als Zusammenfassung all dieser Optionen verwendet.
Leben ohne OFFSET
Stellen wir uns nun vor, wie unsere Welt ohne all diese Probleme aussehen wĂŒrde. Es stellt sich heraus, dass das Leben ohne Offset nicht so schwierig ist: Mit einer Auswahl können Sie nur die Zeilen auswĂ€hlen, die wir noch nicht gesehen haben (Hinweis: diejenigen, die nicht auf der vorherigen Seite waren), indem Sie eine Bedingung in where verwenden.
In diesem Fall gehen wir davon aus, dass Auswahlen auf einer geordneten Menge (gute alte Reihenfolge nach) ausgefĂŒhrt werden. Da wir eine geordnete Menge haben, können wir einen ziemlich einfachen Filter verwenden, um nur die Daten zu erhalten, die hinter dem letzten Datensatz der vorherigen Seite liegen:
SELECT ...
FROM ...
WHERE ...
AND id < ?last_seen_id
ORDER BY id DESC
FETCH FIRST 10 ROWS ONLYDas ist das ganze Prinzip dieses Ansatzes. NatĂŒrlich macht es mehr SpaĂ, wenn man nach vielen Spalten sortiert, aber die Idee ist immer noch dieselbe. Es ist wichtig zu beachten, dass dieses Design auf viele anwendbar ist -Entscheidungen.
Dieser Ansatz wird als Suchmethode oder Keyset-Paginierung bezeichnet. Es löst das Problem des schwebenden Ergebnisses (Hinweis: die zuvor beschriebene Situation beim Schreiben zwischen SeitenlesevorgĂ€ngen) und arbeitet natĂŒrlich, was wir alle lieben, schneller und stabiler als der klassische Offset. StabilitĂ€t liegt darin, dass die Bearbeitungszeit der Anfrage nicht proportional zur Anzahl der angeforderten Tabellen zunimmt (Hinweis: Wenn Sie mehr ĂŒber die Arbeit verschiedener AnsĂ€tze zur Paginierung erfahren möchten, können Sie dies tun . Dort finden Sie auch vergleichende Benchmarks fĂŒr verschiedene Methoden.
Eine der Folien dass die Paginierung nach Tasten natĂŒrlich nicht allmĂ€chtig ist â sie hat ihre Grenzen. Das Wichtigste ist, dass sie nicht in der Lage ist, zufĂ€llige Seiten zu lesen (Hinweis: inkonsistent). Im Zeitalter des endlosen Scrollens (Anmerkung: im Frontend) ist dies jedoch kein so groĂes Problem. Die Angabe einer Seitenzahl zum Anklicken ist im UI-Design ohnehin eine schlechte Entscheidung (Anmerkung: Meinung des Autors des Artikels).
Was ist mit den Werkzeugen?
Die Paginierung auf Tasten ist oft nicht geeignet, da diese Methode nicht instrumental unterstĂŒtzt wird. Bei den meisten Entwicklungstools, einschlieĂlich verschiedener Frameworks, können Sie nicht genau auswĂ€hlen, wie die Paginierung durchgefĂŒhrt werden soll.
Erschwerend kommt hinzu, dass die beschriebene Methode eine durchgĂ€ngige UnterstĂŒtzung der eingesetzten Technologien erfordert â vom DBMS bis zur AusfĂŒhrung einer AJAX-Anfrage im Browser mit endlosem Scrollen. Anstatt nur die Seitenzahl anzugeben, mĂŒssen Sie jetzt einen SchlĂŒsselsatz fĂŒr alle Seiten gleichzeitig angeben.
Allerdings wĂ€chst die Zahl der Frameworks, die die Paginierung auf SchlĂŒsseln unterstĂŒtzen, allmĂ€hlich. Folgendes haben wir im Moment:
- fĂŒr Java;
- fĂŒr Ruby;
- Đž fĂŒr Django;
- fĂŒr Python;
- â Kriterien-API fĂŒr JPA-Implementierungen;
- fĂŒr Perl;
- , Mapper fĂŒr Node.js .
(Hinweis: Einige Links wurden entfernt, da zum Zeitpunkt der Ăbersetzung einige Bibliotheken seit 2017â2018 nicht mehr aktualisiert waren. Bei Interesse können Sie sich die Originalquelle ansehen.)
In diesem Moment wird Ihre Hilfe benötigt. Wenn Sie ein Framework entwickeln oder unterstĂŒtzen, das die Paginierung in irgendeiner Weise nutzt, dann bitte ich Sie, ich fordere Sie auf, ich flehe Sie an, native UnterstĂŒtzung fĂŒr die Paginierung auf Tasten bereitzustellen. Wenn Sie Fragen haben oder Hilfe benötigen, helfe ich Ihnen gerne weiter (, , ) (Anmerkung: Aus meiner Erfahrung mit Marcus kann ich sagen, dass er wirklich begeistert davon ist, dieses Thema zu verbreiten.)
Wenn Sie vorgefertigte Lösungen verwenden, die Ihrer Meinung nach UnterstĂŒtzung fĂŒr die Paginierung nach SchlĂŒsseln wert sind, erstellen Sie eine Anfrage oder bieten Sie nach Möglichkeit sogar eine vorgefertigte Lösung an. Sie können auch auf diesen Artikel verlinken.
Fazit
Der Grund dafĂŒr, dass ein so einfacher und nĂŒtzlicher Ansatz wie die Paginierung nach SchlĂŒsseln nicht weit verbreitet ist, liegt nicht darin, dass er technisch schwierig umzusetzen ist oder groĂen Aufwand erfordert. Der Hauptgrund dafĂŒr ist, dass viele es gewohnt sind, Offset zu sehen und damit zu arbeiten â dieser Ansatz wird durch die Norm selbst vorgegeben.
Infolgedessen denken nur wenige Menschen darĂŒber nach, den Ansatz zur Paginierung zu Ă€ndern, und aus diesem Grund entwickelt sich die instrumentelle UnterstĂŒtzung durch Frameworks und Bibliotheken nur unzureichend. Wenn Ihnen die Idee und das Ziel der versatzfreien Paginierung am Herzen liegen, helfen Sie mit, sie zu verbreiten!
Source:
Autor: Markus Winand
Source: habr.com
