Laden...

Umgang mit großen Datenmengen in einer Web-Anwendung in Kombination mit Echtzeitaktualisierung

Erstellt von UndercoverDeveloper vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.075 Views
U
UndercoverDeveloper Themenstarter:in
16 Beiträge seit 2015
vor 3 Jahren
Umgang mit großen Datenmengen in einer Web-Anwendung in Kombination mit Echtzeitaktualisierung

Hallo zusammen,

wir haben den folgenden Use Case zu entwickeln:

Eine Webanwendung, die eine Datentabelle mit bis zu 100.000 Zeilen und 30-50 Spalten anzeigt. Jede Spalte ist filterbar und sortierbar. Die Webanwendung basiert auf Angular und PrimeNG (mit PrimeNG Table) und das Backend basiert auf ASP.Net Core WebApi, aber ich denke, das spielt keine Rolle.

Die Benutzer sind sehr pingelig, daher ist die Performance eine Top-Priorität, und die Anwendung sollte skalierbar sein. Das Filtern einer Tabelle mit 100.000 Zeilen sollte idealerweise weniger als 5 Sekunden dauern.

Es gibt zwei Besonderheiten in unserer Umgebung:

  • Der Datensatz für die Webanwendung wird in einer SQL-Stored procedure generiert. Die komplexe Logik zur Aufbereitung der Daten befindet sich also nicht in der API selbst.

  • Die Webanwendung wird Echtzeitkommunikation über SignalR implementieren. Wenn also ein Benutzer Daten ändert, erhalten alle anderen Benutzer diese Änderungen sofort.

Was wir vorläufig ausgeschlossen haben..

Im Moment denke ich, dass es keine gute Idee ist, den gesamten Datensatz von der API herunterzuladen und den Client das ganze Filtern/Sortieren übernehmen zu lassen.

Alles würde im Javascript - Code passieren. Und ich kann mir nicht vorstellen das dies bei großen Datenmengen noch schnell ist. Zudem hängt es auch vom PC des Endnutzers ab.

Deshalb dachten wir an das gute alte Paging.

Unsere Idee

Ich habe diesen Prozess vor Augen:

Web Client sendet Anfrage an API -> API ruft SQL-Stored Procedure auf und speichert das Ergebnis in einem Cache Store wie Redis oder ähnlichem und gibt dann die erste Page (die ersten 100 Zeilen) an den Client zurück.

Jetzt, wenn der Benutzer die Daten filtert / sortiert oder zu einer anderen Page wechselt:

Web Client sendet Anfrage an API mit der "CacheId" (oder etwas, um den Datensatz im Cache zu identifizieren) -> Wenn die API eine CacheId erhält, versucht sie zuerst, die Daten aus dem Cache zu laden, anstatt die SQL Stored Procedure erneut aufzurufen. -> Die API wendet Filter, Sortierung, Paging usw. an und gibt dann das Ergebnis an den Client zurück.

Auf diese Weise wird die SQL-Stored Procedure (der teure Part) nicht jedes Mal aufgerufen und auch nur ein kleiner Teil der Daten wird an den Client übertragen.

Natürlich entstehen bei dieser serverseitigen Lösung aber auch mehr Anfragen an die API, welche natürlich auch einen gewissen Zeit-Overhead besitzen.

Unser Hauptproblem / Sorgen

Wie soll die Echtzeit-Kommunikation mit dieser Architektur gehandhabt werden? Wenn jemand etwas an den Daten ändert, müssen alle zwischengespeicherten Datensätze aktualisiert werden. Es ist keine Option, die Datensätze im Cache ungültig zu machen und von Grund auf neu zu generieren, wenn jemand irgendwas ändert. Da Datenänderungen sehr häufig (jede Minute) auftreten wäre es sehr rechenintensiv ständig den kompletten Cache neu zu generieren.

Wir brauchen also so etwas wie das hier:

Benutzer ändert Daten und sendet "DataChanged-Event" -> Datensätze werden aus dem Cache gelesen -> Datensätze werden aktualisiert und dann in den Cache zurückgeschrieben

Unsere Fragen

  • Sind wir mit unserer Lösung auf dem richtigen Weg, oder machen wir etwas völlig falsch? Sollten wir doch alles Client-seitig machen? Client-seitig hätte natürlich den Vorteil von generell weniger API-Anfragen und weniger Netzwerklatenz, weil nur einmal ein Zugriff erfolgt um die Daten zu laden. Andererseits hat man dann wieder die Client-seitigen Performance Limitierungen.

  • Wo sollen wir den Cache-Update-Prozess ansiedeln? Vielleicht eine Azure-Function / Konsolenanwendung oder ähnliches, das alle "DataChanges Events" empfängt und dann den Cache aktualisiert?

Wir würden uns über ein paar Anregungen freuen 😃

H
48 Beiträge seit 2020
vor 3 Jahren

Wenn ihr im SQL Server indexed views verwenden könnt dann braucht ihr gar keinen cache sondern könnt direkt auf die indexierten daten zugreifen. das ist eine besondere fähigkeit vom SQL server. ich halte es aber für fragwürdig wieso ein web client eine tabelle mit 100.000 daten anzeigen soll.

U
UndercoverDeveloper Themenstarter:in
16 Beiträge seit 2015
vor 3 Jahren

Wenn ihr im SQL Server indexed views verwenden könnt dann braucht ihr gar keinen cache sondern könnt direkt auf die indexierten daten zugreifen. das ist eine besondere fähigkeit vom SQL server. ich halte es aber für fragwürdig wieso ein web client eine tabelle mit 100.000 daten anzeigen soll.

Danke ich schau mir das mit den Indexed views mal an.

Natürlich müssen nicht alle 100.000 Datensätze immer dem User angezeigt werden. Wahrscheinlich sind immer nur rund 100 relevant. Das ganze Dataset muss aber für den User filterbar sein.

Damit ich alle Daten filtern kann muss ich erstmal alle Datensätze generieren. Wobei das hier vielleicht auch wieder ein Sonderfall bei uns ist. Die Daten die wir dem Benutzer da anzeigen sind stark aufbereitet und liegen nicht so in dieser Form filterbar in der Datenbank vor wie der User sie am Ende sehen soll / filtern können soll.

Das initiale Generieren dieser 100.000 Datensätze durch die Stored Procedure dauert vielleicht etwas mehr als 30 Sekunden. Das kann ich dem Nutzer aber verkaufen wenn er nur einmal diese 30 Sekunden warten muss bis alles generiert ist aber er dann relativ adhoc in den Daten rumfiltern kann.

Deswegen war auch meine Idee das der Server bzw. die API alles handelt. Also das Ergebnis der Stored Procedure im Cache speichert, die Filterung ausführt und dann die Top 100 Zeilen zurückgibt. Bei einer erneuten Filterung muss dann nur noch auf den Cache zugegriffen werden.

T
2.224 Beiträge seit 2008
vor 3 Jahren

@UndercoverDeveloper
So würde ich es vermutlich auch machen.
Wenn die Daten eh für alle Benutzer gleich sind, kann man diese in der Api cachen.
Wenn es Änderungen gibt, müssen diese nur in den aktuellen Cache eingearbeitet werden.
Im besten Fall sollte der Cache einmal beim Start der Web Anwendung aufgebaut werden und für die Laufzeit gehalten werden.

Ich würde die Filterung und Sortierung dann z.B. per Linq machen.
Als Datenquelle würe eigentlich eine stumpfe List<T> reichen.
Du musst dann nur für deine Datensätze eine Klasse anlegen um diese abzubilden.

Je nachdem was für Clients du hast, könntest du die Filterung/Sortierung aber auch im Client via JS machen.
Dadurch sparst du dir vermutlich auch ordentlich Traffic zwischen Clients und API.
100.000 Datensätze sollte auch für heutige Clients kaum ein Problem sein.
Durch promises in JS kann die Filterung/Sortierung dann auch asynchron laufen.
Bzw. sollte das richtige Framework im Client das auch schon lösen können.
Bin hier leider nicht mehr auf dem aktuellen Stand, weshalb Empfehlungen von mir leider nicht drin sind.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

U
UndercoverDeveloper Themenstarter:in
16 Beiträge seit 2015
vor 3 Jahren

@T-Virus

Danke dir.

Alles Client-seitig zu machen würde vieles vereinfachen. Ich habe nur die Sorge das es nicht skalierbar ist. Vielleicht sind es irgendwann 200.000 Datensätze oder mehr.

Zudem soll die Möglichkeit bestehen die Webanwendung auch auf einem Tablet oder Smartphone zu verwenden. Hier habe ich dann Client-seitig nochmal eine geringere Leistung zur Verfügung und die Datenmenge zwischen Client und Server spielt eine größere Rolle. Mobile Nutzung ist jetzt keine Top-Priorität aber ich darf es auch nicht komplett ausschließen.

Schwierig 😁 Ich glaube ich muss mal ein Testprojekt machen um zu sehen ab welcher Datenmenge man Client-seitig an Grenzen kommt.

F
10.010 Beiträge seit 2004
vor 3 Jahren

Sind die 100.000 Datensätze für jeden benutzer verschieden?
Wie viele Benutzer?

U
UndercoverDeveloper Themenstarter:in
16 Beiträge seit 2015
vor 3 Jahren

Sind die 100.000 Datensätze für jeden benutzer verschieden?
Wie viele Benutzer?

Grundsätzlich sind sie gleich, die Datensätze sind in Kategorien / Projekten angeordnet. Also der Benutzer wählt aus, aus welchem Projekt er sich die Datensätze anzeigen lassen möchte. In dem einen Projekt können 5000 Datensätze sein, mal 10.000 und eben einzelne Extremfälle mit 100.000.

Das System sollte 50-100 Nutzer gleichzeitig handeln können.

Ich habe jetzt aber einfach mal ein Testprojekt erstellt indem eine Tabelle mit 30 Spalten angezeigt wird die man filtern und sortieren kann und bin positiv überrascht.

1.000000 - 5.000000 Datensätze:

Sehr schnell. Kaum Verzögerung beim filtern.

5.000000 - 20.000000 Datensätze:

Verzögerung aber noch halbwegs akzeptabel

Ab 25.000000 Datensätzen bekomme ich OutOfMemory - Fehler in Chrome. Aber okay, solch ein Szenario kalkulieren wir auch nicht. Ich habe auch nur mit Datensätzen im Millionenbereich getestet um wirklich mal die Grenzen zu ermitteln.

Dann sollten 100.000 oder auch mal 200.000 - 300.000 Datensätze kein Problem sein. Ich habe da vielleicht die modernen Browser und Javascript etwas unterschätzt. Mittlerweise sind die ja doch recht leistungsfähig. Wir switchen gerade von Desktop auf Webentwicklung und müssen so Sachen daher erstmal evaluieren.

Ich denke, wir werden dann erstmal den Weg gehen dem Client die Filterung und Sortierung zu überlassen. Das macht die ganze Logik im Backend wesentlich einfacher und kommt der Entwicklungszeit zu gute. Es erzeugt auch generell weniger API Requests.

Die serverseitige Lösung mit dem Caching behalten wir mal im Hinterkopf, falls die Anforderungen doch noch steigen.

5.658 Beiträge seit 2006
vor 3 Jahren

An welcher Stelle genau gibt es denn überhaupt Performance-Probleme? Beim Berechnen der Daten? Bei der Übertragung? Oder bei der Darstellung? Oder geht es dir darum, wie man den Client über geänderte Datensätze informieren kann?

Weeks of programming can save you hours of planning