C# REST API: Umgang mit großer Liste

DrCox1911

Lt. Junior Grade
Registriert
Juni 2018
Beiträge
501
Servus zusammen,

ich habe gerade eine REST API, die aus einer Oracle-DB ca. 20k-100k Einträge liest. Diese Einträge sollen an ein Blazor Server Frontend übergeben werden. Die Menge an Datensätze ist wohl zu groß, um es einfach so als List<UnitModel> zurückzugeben.

Wie handhabe ich denn am besten eine so große Datenmenge?
Abgerufen werden die Daten mittels Dapper aus der Oracle-DB und dann entsprechend auf eine List<UnitModel> gemappt.
 
Ich würde mal zum Stichwort "Lazy Loading" suchen, wenn ich das Problem richtig verstanden habe. Wie man das in C# implementiert, weiß ich leider nicht.

Gruß Jens
 
  • Gefällt mir
Reaktionen: Autokiller677 und KitKat::new()
Ansonsten pagination anschauen. Ist bei APIs ueblich, dass man bei vielen Eintragen mehrmals anfragen muss, um immer weitere X elemente zu erhalten.
 
  • Gefällt mir
Reaktionen: mental.dIseASe, pcBauer, Autokiller677 und eine weitere Person
Wenn du dafür etwas fertiges suchst, könntest du dir mal OData anschauen. Das ist ein sehr häufig verwendeter Standard zum filtern, sortieren und eben paging von Collections. Sofern deine Datenquelle ein IQueryable ist, funktioniert das in C# sogar out of the box, ohne dass du selbst etwas implementieren müsstest.
https://docs.microsoft.com/de-de/odata/
 
Kann das Frontend überhaupt diese Datenmengen verarbeiten? Grundsätzlich würde ich die Daten auf dem Backend schon so vorbereiten, dass sie genau dem entsprechen was das Frontend haben will - also z.B. Aggregationen auf dem Backend ausführen.

Ansonsten gibt's Pagination oder Streaming, um die Datensätze Stück für Stück zu übertragen.
 
Bei odata ggf aufpassen, das MS über die Jahre mehrmals die Richtung geändert hat, zwischenzeitlich zu .NET Core 2.x Zeiten hat da nicht viel funktioniert, teilweise auch weil EF core schwer hinterher hinkte. Hat sich mittlerweile aber wohl gebessert.
Ansonsten sicherstellen, dass du sinnvolle Begrenzungen bei odata einbaust, ansonsten kann man damit zügig deinen DB Server zum erliegen bringen.

Für eine primitive paging Api wäre odata wohl Overkill. Aber wenn du so viele Werte direkt via Blazor anzeigen willst brauchst du ggf früher oder später auch Sorting usw und dann mag odata wieder ein besserer Ansatz sein. Als erstes würde ich mir ernsthaft die Frage stellen wieso du soviele Werte in einem Browser anzeigen willst.
 
Ich würde grundsätzlich Endpunkte die Listen rausgeben mit Pagination machen, wenn die Listen nicht inhärent limitiert sind.

In Dapper musst du einfach die richtigen LIMIT und OFFSET Klauseln einfügen (oder was auch immer das Aquivalent in Oracle ist). Man muss da aber berücksichtigen das sich die Daten verschieben können wenn die Liste sich ändert, das muss der Client handlen können falls es bei diesen Daten passieren kann. Es gibt zu Pagination noch andere Methoden, aber das ist die einfachste.
 
Bin da im Team "Alles ab 1000 Einträgen ist am Client vollkommen nutzlos und sollte das Backend so auch niemals verlassen."
 
  • Gefällt mir
Reaktionen: mental.dIseASe, Fombu und NJay
Danke für all die Antworten. Leider braucht der Client aber tatsächlich all diese Einträge, da es sich hierbei um die zuletzt gebuchten Geräte handelt und diese für eine Einbuchung in ein anderes System benötigt werden. An der Oracle-DB selbst kann ich dabei nichts ändern, die ist so vorgegeben und ich habe nur Leserechte, um mir entsprechend die Daten zu ziehen.

Die Einträge werden dann mit einer Datenbank, die ich unter Kontrolle haben, verglichen. Also ein Teil der Einträge aus der Oracle-DB befinden sich also auch in meiner DB, ich muss dann darstellen, welche Einträge in beiden sind und welche nur in der Oracle-DB.
 
Du kannst doch trotzdem, unverändert, die Daten über Pagination holen? Das ist zumindest weitaus sinnvoller, als 100k Einträge mit einmal zu holen und sicherlich auch schneller am Ende. Über die eingesparte, mögliche, Last - reden wir erst nicht.
 
  • Gefällt mir
Reaktionen: NJay
cfHxqA schrieb:
Du kannst doch trotzdem, unverändert, die Daten über Pagination holen? Das ist zumindest weitaus sinnvoller, als 100k Einträge mit einmal zu holen und sicherlich auch schneller am Ende. Über die eingesparte, mögliche, Last - reden wir erst nicht.

Sehe ich auch so. Selbstw enn du viele Daten brauchst, das ganze mit Pagination zu loesen ist sinnvoll.
 
Ja, das mit pagination schaue ich mir auch an, wollte mit meinem Beitrag nur verdeutlichen, wofür die vielen Einträge brauche.
 
  • Gefällt mir
Reaktionen: cfHxqA und NJay
Ein anderer Ansatz wäre evtl die Daten zu tippen o.ä. Gibt Kompressionsmethoden die auch ohne nennenswerte Zeitverzögerung arbeiten.

Bei der Pagination musst Du das Ergebnis wahrscheinlich serverseitig zwischenspeichern, falls sich während der Pagination die zugrunde liegenden Daten ändern.
 
Drexel schrieb:
Ein anderer Ansatz wäre evtl die Daten zu tippen o.ä. Gibt Kompressionsmethoden die auch ohne nennenswerte Zeitverzögerung arbeiten.

Bei der Pagination musst Du das Ergebnis wahrscheinlich serverseitig zwischenspeichern, falls sich während der Pagination die zugrunde liegenden Daten ändern.
Zu tippen? 🤨

Das kommt aufs gleiche hinaus, wenn Du die Daten auf einmal holst. Theoretisch kann eine Änderung jederzeit eintreten. Das Risiko gehst Du mit jeder Datenbankabfrage ein. Somit völlig irrelevant.

Sollten die Daten in Echtzeit relevant sein, müsste die Datenbankverbindung das triggern von Events erlauben, um zu schauen, ob Datensätze aktualisiert worden sind. Teufelskreis..
 
Zippen sorry, da hat die Autokorrektur am Handy zugeschlagen.

Ja natürlich können sich Daten ständig ändern, aber wenn ich nur eine Abfrage habe, die mir alle Daten zurückgibt hab ich aber ein für den Moment gültiges Result Set.

Je nachdem wie man die Pagination aufbaut kann es zu Schwierigkeiten kommen. Z.B. wenn die erste Anfrage 10 Seiten zurückgibt, während der Pagination aber neue Daten zukommen und es auf einmal mehr als 10 Seiten gibt und je nach Sortierung sich auch noch neue und alte Datensätze mischen und 2 Datensätze auf einmal doppelt sind, weil sie von den Seiten verrutscht sind.

Will nur sagen: man muss das mal durchdenken und sich über die Konsequenzen im Klaren sein, die Komplexität steigt auf jeden Fall gegenüber einer einfachen Abfrage...
 
Aber Du kannst doch mit deiner Anfrage die Daten gleich sortieren? Natürlich entsprechend nach ID o.Ä. Damit wirst die Wahrscheinlichkeit einer Dopplung verhindern. Andernfalls wäre es im Quellcode zu lösen, ist wohl das kleinere übel.

Das holen aller Daten erzeugt aber ungemein eine riesen große Last, je nach Datenmenge. Das nächste ist, ob der Server das dann überhaupt noch mit macht. Du hast viele Vor- und Nachteile, was jeweils für beide Spricht.

Btw. Datensätze können schnell mehrere GB zurückgeben, je nach Anzahl der Felder und Inhalte... Es ist also Stark abzuwiegen, was ich tatsächlich dem Server bzw. dem Nutzer zumute. Und bei einer großen Menge, wird auch garantiert eher der Nutzer ein Problem kriegen mit der Anwendung, als der Server.
 
DrCox1911 schrieb:
Danke für all die Antworten. Leider braucht der Client aber tatsächlich all diese Einträge, da es sich hierbei um die zuletzt gebuchten Geräte handelt und diese für eine Einbuchung in ein anderes System benötigt werden. An der Oracle-DB selbst kann ich dabei nichts ändern, die ist so vorgegeben und ich habe nur Leserechte, um mir entsprechend die Daten zu ziehen.

Die Einträge werden dann mit einer Datenbank, die ich unter Kontrolle haben, verglichen. Also ein Teil der Einträge aus der Oracle-DB befinden sich also auch in meiner DB, ich muss dann darstellen, welche Einträge in beiden sind und welche nur in der Oracle-DB.

Du brauchst dazu nicht alle Daten auf einmal, ich würde mir weniger Sorgen um den Transfer und die DB machen als den Client und evtl das Backend, wenn der 100k Einträge gleichzeitig verarbeitet. Die DB streamt das im Idealfall, das Backend nicht unbedingt und im Client würde die ganze Liste im Speicher materialisiert werden. Wenn die Einträge klein sind ist das noch ok, aber das kann recht ineffizient werden.

Wenn es möglich ist würde ich nur die IDs bzw. andere Merkmale nach denen du identische Einträge identifizierst aus der DB holen im ersten Schritt. 100k IDs ist wesentlich schlanker als 100k Objekte. Allerdings auch nur unter der Annahme das der größte Teil der Einträge schon in deiner DB ist, und du nicht die komplette Information brauchst für diese.

Wenn das ganze nur ein Batch Job ist der ab und zu läuft und auch mal ein bißchen dauern kann dann kann man das sicher auch einfach mit brute force machen. Aber wenn es effizienter und schneller gehen soll dann wäre es ideal irgendeine Regelmäßigkeit in den Datensätzen zu haben um redundante Vergleiche zu vermeiden.
 
Ok, ich habe heute den SQL, den ich von der Datenbank-Truppe bekommen habe, soweit optimiert, dass die Abfrage deutlich schneller geht. Ebenso habe ich Paging in die SQL-Abfrage mit eingebaut, damit kann ich jetzt 150 distinct Einträge in 8 Sekunden abrufen, was für meinen Zweck passt. Hätte dann an 50 Einträge pro Seite am Client gedacht, d.h. ich habe drei Seiten geladen, die aktuell angezeigte, die vorherige und die nächste Seite. Wenn der User dann die Seiten navigiert, hätte ich im Hintergrund die entsprechend nächsten Einträge geladen.

Soweit so gut, allerdings dauert bei mir dann die Abfrage über die eigentliche API ewig (5 Minuten pro 150 Einträge in Swagger). Denke das "Bauen" der JSON dauert so lange?

Abgerufen werden die Daten mittels Dapper
 
DrCox1911 schrieb:
150 distinct Einträge in 8 Sekunden abrufen
Find ich persönlich Arschlangsam, ich kenn natürlich das Query und das Schema nicht. Aber 8 Sekunden, da zieh ich bei mir in einem Bruchteil der Zeit ein Vielfaches raus auch in komplexeren Auswertungen. Erreichst du diese Zeit auch, wenn du das Query direkt auf der DB ausführst? (also nicht mit irgendwelchen Schnittstellen oder Frameworks davor). Wenn ja, könnte die Datenbanktruppe das ganze noch optimieren.


DrCox1911 schrieb:
die Abfrage über die eigentliche API ewig (5 Minuten pro 150 Einträge in Swagger). Denke das "Bauen" der JSON dauert so lange?
Irgendwo habt Ihr ein Performance Problem. Entweder ist das System massiv überlastet, falsch konfiguriert oder euer Schema Müll. Ich habe noch nie mit Swagger gearbeitet, aber so langsam ... das kann ich mir nicht vorstellen. Ein JSON Objekt zu erstellen ist auch nichts was lang dauert. Es wird nur das Ergebnis in JSON gewandelt?

Im Summe wenig Hilfe, ich selbst kann allerdings aus den Informationen keinen gescheiten Tipp geben außer Stück für Stück zu schauen wo die Leistung verloren geht. 150 Einträge in 5 Minuten und du hast 20-100k ... da ist der Arbeitstag schnell um.
 
Zurück
Oben