Wir brauchen Tool-Unterstützung zum Blättern mit Keysets


Wusstest du, dass Blättern mit offset sehr problematisch, aber einfach zu vermeiden ist?

offset sagt der Datenbank die ersten N Ergebnisse eine Abfrage zu überspringen. Die Datenbank muss diese Ergebnisse aber trotzdem laden und in die gewünschte Reihenfolge bringen, bevor sie die darauffolgenden Ergebnisse senden kann.

Das ist kein Problem der Implementierung, sondern ein Design-Problem von offset:

…the rows are first sorted according to the <order by clause> and then limited by dropping the number of rows specified in the <result offset clause> from the beginning…

In anderen Worten: Große offsets verursachen einen großen Aufwand für die Datenbank – unabhängig ob SQL oder NoSQL.

Es gibt aber noch andere Probleme, wenn man mit offset blättert: Was passiert wenn, zwischen dem Laden zweier Seiten ein neuer Eintrag eingefügt wird?

Wenn man offset nutzt➌, um die zuvor geladenen Einträge❶ zu überspringen, erhält man Duplikate, wenn in der Zwischenzeit neue Einträge gespeichert wurden➋. Natürlich gibt es durch offset noch andere Anormalitäten, dies ist aber die Häufigste.

Das ist auch kein Datenbank-spezifisches Problem – es wird vielmehr dadurch verursacht, dass beim Blättern Seitennummern verwendet werden. Nur mit dieser Information kann es keine Datenbank besser machen.

Das Leben ohne OFFSET

Und nun stell dir eine Welt vor, in der es diese Probleme nicht gibt. Es stellt sich sogar heraus, dass das Leben ohne offset sehr einfach ist: Dazu muss man nur eine where-Klausel verwenden, die nur die Daten selektiert, die man noch nicht gesehen hat.

Dafür nutzt man einfach aus, dass man mit sortieren Daten arbeitet – du hast doch eine order by-Klausel, oder? Sobald man eine eindeutige Sortierreihenfolge hat, kann man mit einem einfachen Filter nur das auswählen, was nach dem letzten Eintrag kommt, den wir gesehen haben:

SELECT ...
  FROM ...
 WHERE ...
   AND id < ?last_seen_id
 ORDER BY id DESC
 FETCH FIRST 10 ROWS ONLY

Das ist das Grundrezept. Es wird etwas interessanter, wenn man über mehrere Spalten sortiert, aber die Idee bleibt dieselbe. Das Rezept lässt sich natürlich auch auf NoSQL-Systeme anwenden.

Dieser Ansatz – die Seek-Methode, auch keyset pagination genannt – löst das Problem von doppelten Ergebnissen und ist obendrein noch schneller als offset. Wenn du sehen willst, was sich bei offset und keyset pagination in der Datenbank abspielt, sieh dir doch diese Slides an:

In den Slides wird auch gezeigt, dass SQL eine praktische Funktion hat um alles, das nach etwas bestimmten einsortiert wird, zu selektieren: die row-value Vergleiche. Leider werden sie kaum unterstützt. Das Problem lässt sich aber leicht umgehen – man muss die where-Klausel nur sehr sorgfältig verfassen. Die Details findest du in diesem Artikel. Wenn du Blättern ohne offset umsetzen möchtest, musst du diesen Artikel vorher unbedingt lesen.

Aber die Frameworks…

Der Hauptgrund, dennoch offset zu verwenden, ist, dass die Seek-Methode (keyset pagination) von keinem gängigen Framework unterstützt wird. Die meisten Tools unterstützten offset, aber keinen einfachen Weg auch keyset pagination umzusetzen.

Bitte beachte, dass keyset pagination den ganzen Technologie-Stack bis zum JavaScript im Browser, das mittels AJAX die nächsten Ergebnisse beim „unendlichen Scrollen“ holt: Anstatt einer Seitennummer muss man dem Backend nämlich einen Schlüssel des zuletzt geladenen Eintrages übergeben.

Nur wenige Werkzeuge können sich derzeit damit rühmen, keyset pagination angemessen zu unterstützen:

Hier brauche ich deine Hilfe. Wenn du ein Framework wartest, das etwas mit Blättern zu tun hat, dann bitte ich dich, ich dränge dich, ich flehe dich an, ebenfalls Unterstützung für keyset pagination einzubauen. Wenn du irgendwelche Fragen zu den Details hast, helfe ich gerne (Forum, Kontakt-Formular, Twitter)!

Selbst wenn du nur Nutzer eines Frameworks bist, das keyset pagination umsetzen sollte, mach die Entwickler bitte darauf aufmerksam. Du könntest es einfach als neue Funktion vorschlagen, oder, falls möglich, einen Patch dafür liefern. Noch mal: Bei den Details helfe ich gerne.

Bitte weitersagen

Das Problem mit keyset pagination ist nicht technisch. Das Problem ist, dass es kaum bekannt ist und von den Tools nicht unterstützt wird. Wenn du die Idee magst, ohne offset auszukommen, dann erzähl es bitte weiter. Tweet es, mag es, mail” es, du kannst diesen Blog-Eintrag sogar re-bloggen (CC-BY-NC-ND).

Falls du selbst bloggst, könntest du auch einen NoOffset-Banner auf deinen Blog stellen, damit deine Leser auch Bescheid wissen. Ich habe eine ganze Gallerie von NoOffset-Bannern vorbereitet – nimm einfach, was passt.

Über den Autor

Photo of Markus Winand
Markus Winand stimmt Entwickler auf SQL-Performance ein. Er hat das Buch SQL Performance Explained veröffentlicht und bietet inhouse Schulungen sowie Tuning-Leistungen auf http://winand.at/ an.