Bei meiner langjährigen Tätigkeit als Softwareentwickler habe ich ein Performanceproblem klar am meisten angetroffen:
Zu viele Netzwerkaufrufe!
Es kann sich um Aufrufe einer Datenbank, eines Webservers, eines Application Servers oder einer beliebigen Schnittstelle handeln.
Jeder Netzwerkaufruf benötigt relativ viel Zeit verglichen mit vielen anderen Verarbeitungen.
Ob es nun ein oder zwei Netzwerkaufrufe gibt, ist nicht so relevant. Aber wenn innerhalb von Schleifen Netzwerkaufrufe ausgeführt werden, kann dies schnell zu krassen Performanceproblemen führen.
Auch Lazy Loading, welches Daten über das Netzwerk anfordert, kann zu grossen Performanceproblemen führen. Den Lazy Loading kann sehr viele Netzwerkaufrufe auslösen.
Wie viel Zeit brauchen nun viele Netzwerkaufrufe wirklich?
Ich habe eine Applikation erstellt, welche 40 Byte grosse Objekte abruft.
Die folgende Tabelle zeigt, wie lange das im lokalen Netzwerk über WLAN gedauert hat mit zwei verschiedenen Abrufarten:
| Anzahl Objekte | Netzwerkaufruf pro Objekt | Alle Objekte in einem Netzwerkaufruf |
| 10 | 32 ms | 2.1 ms |
| 100 | 217 ms | 3.0 ms |
| 1’000 | 2’693 ms | 8.4 ms |
| 10’000 | 21’855 ms | 41.7 ms |
| 100’000 | 232’559 ms | 231 ms |
Durch weniger Netzwerkaufrufe wird die Abfrage auch bei nur 10 Objekten schon viel schneller. Bei 100’000 Objekten wurde die Abfrage sogar um den unglaublichen Faktor 1’007 schneller!
Die Netzwerklatenz ist die Zeit, welche Daten benötigen, um von einem Gerät zu einem anderen zu gelangen. Bei jedem Netzwerkaufruf entsteht diese Latenzzeit. Die Latenz multipliziert mit der Anzahl Netzwerkaufrufe ist somit eine sehr entscheidende Angabe.
Die Netzwerklatenz ist stark davon abhängig, wie weit die Daten transportiert werden müssen.
Die folgende Tabelle zeigt grobe Richtbereiche für die Latenz bei verschiedenen Distanzen. Diese Bereiche können auch merklich über oder unterschritten werden. Wo nichts anderes vermerkt ist, handelt es sich um eine Verbindung über Kabel.
| Distanz | Typische Netzwerklatenz für Round‑Trip |
| Lokal (gleiches Gerät) | 0.05 – 0.1 ms |
| Lokales Netzwerk | 0.2 – 1 ms |
| Lokales Netzwerk über WLAN | 2 – 20 ms |
| Stadt / Regional | 1 – 10 ms |
| National | 2 – 25 ms |
| Europa | 20 – 50 ms |
| Interkontinental | 80 – 250 ms |
Es bestehen also starke Unterschiede – wenn man also z.B. die Mittelwerte von «Lokales Netzwerk» mit Europa vergleicht, ergibt sich ein Faktor von rund 60.
Auch bei einer guten Verbindung sind Schwankungen bei der Latenz normal, während instabile Netzwerke extreme Unterschiede verursachen können. Die Latenz schwankt vor allem Aufgrund von hoher Netzwerkauslastung, Paketverluste, Routing (Daten nehmen einen anderen Weg) und Funkstörungen (WLAN, Mobilfunk).
Wie geht man nun vor, damit nur wenige Netzwerkaufrufe ausgelöst werden? In diesem Kapitel wird dies gezeigt am Beispiel einer Datenbankanbindung.
Das Laden sollte immer so erfolgen, dass sich keine Netzwerkaufrufe innerhalb einer Schleife befinden. Man hat beispielsweise eine Liste von Rechnungen und benötigt auch die Kunden dazu. Hier darf nicht bei jeder Rechnung der Kunde geladen werden. Es müssen alle Kunden in einem Aufruf geladen werden. Oder beim Laden der Rechnungen werden die Kunden gerade mitgeladen.
Oft müssen ganze Listen von Daten eingefügt oder mutiert werden. Dies sollte auch in einem Netzwerkaufruf erledigt werden. Falls es sich immer um wenig Daten handelt, könnte allenfalls eine Ausnahme gemacht werden. Den meisten Datenbanken können direkt mehrere Insert und Update Commands in einem Aufruf übermittelt werden.
«Lazy Loading» kann ein gutes Konzept sein. Aber in einem ORM (Object‑Relational Mapper) sollte dieses Konzept normalerweise nicht verwendet werden, weil damit schnell einmal viele unnötige Netzwerkaufrufe ausgelöst werden.
Hier Hinweise zu Lazy Loading von der ORM Dokumentation von EF Core:

Bei der Entwicklung ist es oft so, dass Netzwerkaufrufe gar nicht über das Netzwerk ausgeführt werden. Sondern dass z.B. die Datenbank oder der Application Server direkt auf dem gleichen Rechner läuft. Diese Aufrufe sind dann meistens deutlich schneller. Dadurch wird die Performance falsch eingeschätzt.
Auf einem produktiven System hat es oft deutlich mehr Daten als bei der Entwicklung verwendet werden. Dadurch werden Performanceproblem oft zu spät erkannt.
Darum sollte mindestens in der Testumgebung mit analogen Datenmengen und Infrastruktur wie im Produktivsystem getestet werden.
Die Anzahl der Netzwerkaufrufe hat meistens einen sehr starken Einfluss auf die Performance. Wenn ORM mit Lazy Loading verwendet werden, kann es sehr schnell passieren, dass zu viele Netzwerkaufrufe ausgeführt werden.
Performanceprobleme werde oft erst nach dem Produktivgang festgestellt. Darum ist es wichtig, dass in der Testumgebung mit analogen Datenmengen und Infrastruktur wie im produktiven System gearbeitet wird.
Schreiben Sie einen Kommentar