Backend Performance - Probleme erkennen und lösen!

Du hast Probleme mit der Performance deiner Anwendung? Werfen wir doch mal einen Blick auf die Stellschrauben der Performance. Lass uns über Sprachen, Caching, Datenbanken und Skalierung sprechen.

Wer nichts wagt, kann auch nichts gewinnen!

Marco Schiffmann
packt jede Gelegenheiten am Schopf und scheut sich nicht vor neuen Herausforderungen
Lesedauer: ca. 6 Minuten

Wir haben bereits in einem früheren Blogpost das Thema Frontend-Performance behandelt. In diesem Artikel geht es um das die Performance im Backend und welche Faktoren für die Anwendungsentwicklung eine Rolle spielen. Was muss beachtet werden und welche Stellschrauben gibt es? 

Berechnung von Pi mit Leibnizformel. Performacne Rust, JS, PHP

Sprachen - PHP, JS, Rust...

Bereits bei der Wahl der Programmiersprache als Basis für die Entwicklung einer Anwendung im Rahmen eines Projektes gibt es erhebliche Unterschiede in der Performance.

Betrachtet man die reinen Zahlen, z.B. bei der Berechnung der "Leibniz-Formel" für eine Annäherung an Pi, wird deutlich, dass Sprachen wie PHP, die wir in vielen unserer Projekte einsetzen, gegenüber JS oder Rust klar im Nachteil sind. In diesem Fall wurde die Berechnung für Pi durch die Leibniz-Formel 1.000.000.000 iteriert. (Quelle: GitHub, Niklas Heer)

Es ist also wichtig, sich zu Beginn eines Projektes ernsthaft mit der Aufgabenstellung für die Anwendung auseinanderzusetzen. Neben der reinen Rechenleistung spielen natürlich noch viele andere Faktoren bei der Auswahl eine Rolle.

Für Rust würde z.B. sprechen, wenn die Anwendung sicherheitskritisch ist (Speicher- und Typsicherheit), Skalierbarkeit eine Rolle spielt (Multithreading) oder eine systemnahe Programmierung entscheidend ist. Alles Bereiche, in denen PHP schlechter abschneidet.

Für viele Kundenprojekte punktet PHP mit einer großen Auswahl an etablierten Frameworks (Laravel, Symfony etc.), einfachem Hosting, höherer Entwicklungs- & Debuggeschwindigkeit und einer dynamischeren Natur bei der Typisierung.

Aus unserer Erfahrung zahlt sich eine genaue Analyse der Zielsetzung der Anwendung zu Beginn des Projektes aus, um ggf. bereits zu diesem Zeitpunkt Geschwindigkeitsvorteile durch die Wahl der Programmiersprache zu nutzen. 

In der Praxis sind in unserem Projektgeschäft jedoch andere Stellschrauben für die Backend-Performance von größerer Bedeutung.

php,js,rust logos
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
Knuth, D. E. (1974). Structured Programming with go to Statements, ACM Computing Surveys, 6(4), 261–301.

Umsysteme

Für die Gesamtperformance einer Anwendung ist es natürlich wichtig, die Infrastruktur bzw. die Umsysteme zu betrachten, in die sie eingebettet ist. Viele Komponenten beeinflussen die Performance. Es gilt das Zusammenspiel von Datenbanken, Caches, Filestorage und Schnittstellen zu untersuchen. Aus unserer Projekterfahrung liegen in diesem Zusammenspiel erhebliche Optimierungspotentiale für eine Anwendung. 

Doch wie gehen wir vor und wo setzen wir an ohne wichtige Projektressourcen für kleine Performancegewinne zu verschwenden? 

Systemübersicht Entwicklung

Optimierungsbedarf erkennen? Wo fangen wir an?

Wie können wir den tatsächlichen Optimierungsbedarf unserer Anwendung erkennen und eingrenzen, um möglichst wenig Ressourcen für unkritische Optimierungen zu verschwenden? Diese Frage stellt sich im Projektalltag immer wieder neu. 


blackfire & Nginx logs

Ein gutes Beispiel für ein Tool in diesem Bereich ist "blackfire", das wir derzeit für unsere PHP-Projekte evaluieren. Durch Continuous Profiling, Live Monitoring, Tests und CI/CD Integration kann die Software Engpässe schnell identifizieren und auch für unsere Entwickler:innen visualisieren. Inwieweit wir die Software in unsere bestehenden CI/CD-Prozesse integrieren werden, bleibt abzuwarten.

Ein weiteres "Tool" bzw. ein guter Indikator, den wir seit einiger Zeit in unseren Projekten einsetzen, ist die Analyse des Varnish Cache-Status aus den Nginx Logs.

Dabei schauen wir uns an, welche Inhalte erfolgreich gecached werden und welche nicht (miss, uncacheable, unconfigured) und beobachten Abweichungen. Auch das "Clustern" der Content-Typen aus dem Nginx-Log nach "request time" gibt bereits erste Rückschlüsse auf die Ursachen von Performance-Problemen in der Anwendung.

balackfire.io, nginx logo

Caching

Ein gut durchdachtes Caching, bei dem nach Cache-Einheiten unterschieden wird, bringt mit moderaten Aufwand einen direkten Performancegewinn.  Es gibt eine ganze Reihe von Cache-Einheiten, in die das Caching unterteilt werden kann. Die wichtigsten wären "Object Caches" und "Full Page Caches", daneben gibt es natürlich noch Query Caches, OpCode Caches und viele mehr.

Es mag trivial klingen, aber in der Praxis hat sich in unseren Projekten gezeigt, dass es sich lohnt, sich die Zeit zu nehmen und die Unterscheidung zwischen dynamischen und statischen Inhalten ist für unsere Kunden nicht immer einfach. Aber die Analyse lohnt sich, um Performancegewinne zu realisieren. 

redis. memcached logo

"Object Caches"

Diese Caches eignen sich für komplizierte, deterministische Operationen als Teil der Anwendungsroutinen, z.B. dynamische Webanwendungen mit personalisierten Inhalten oder dynamischen Daten. Da die Anfrage aus dem Cache abgerufen wird, werden Datenbank und APIs entlastet. Häufig werden hier Redis, Memcached oder APCu eingesetzt.

"Full Page Caches" (FPC)

Bei "Full Page Caches" (FPC) werden, wie der Name schon sagt, komplette gerenderte Seiten (HTML, CSS, JavaScript) zwischengespeichert und direkt ausgeliefert, was eine enorme Entlastung des Webservers und der Datenbank mit sich bringt. Diese Art des Cachings ist perfekt für statische Inhalte, hat aber auch seine Tücken, da es bei Änderungen, z.B. durch die Redaktion, zu Verzögerungen kommen kann, da diese erst im Cache aktualisiert werden müssen. FPC arbeitet mit reinen "GET"-Requests. 

Häufig werden hier Varnish, Nginx oder Fastly eingesetzt. Neben der höheren Performance schützt der Cache in diesem Fall auch vor Erfolgs- oder DOS-Problemen, da nicht direkt auf den Webserver zugegriffen wird.


Datenbanken

Um Performanceprobleme zu vermeiden, ist es sinnvoll, auf relationale Datenbanken (RDB) wie z.B. MySQL, PostgreSQL oder MariaDB zu setzen. Natürlich muss für jede Anwendung geprüft werden, ob relationale oder nicht-relationale Datenbanken eingesetzt werden sollen, im klassischen CMS-, CRM- oder E-Commerce-Bereich kann oft auf relationale Datenbanken zurückgegriffen werden.

Die Vorteile der RDB liegen auf der Hand, durch die Struktur in Tabellenform können Abfragen ("Queries") effizient durchgeführt werden. Noch schneller geht es, wenn zusätzlich "Indizes" definiert werden. Allerdings ist zu beachten, dass auch bei kleinen Änderungen in der Datenbank der Index neu aufgebaut werden muss, was ebenfalls Zeit in Anspruch nehmen kann. Ziel ist es, die Logik in die Datenbank zu verlagern, die dafür am besten geeignet ist und diese sehr effizient abarbeiten kann. Sollten Performanceprobleme in der Datenbank erkannt werden, kann eine pragmatische Lösung auch darin bestehen, einfach den verfügbaren Arbeitsspeicher zu erhöhen.

Ohne unnötig Ressourcen z.B. in eine Code-Optimierung zu verschwenden, kann so bereits auf Abfrage- und Datenbankseite viel Performance gewonnen werden. 

mariadb,PostgresSQL,mySQL Database logos

Skalierung

Vertikale und horizontale Skalierung sind klassische Methoden zur Verbesserung der Performance. Beide Methoden haben ihre Vor- und Nachteile, wirken sich aber erheblich auf die Performance aus und sollten bei Problemen näher betrachtet werden.

Vertikale Skalierung (Scale-Up)

Die vertikale Skalierung (Scale-Up) basiert auf einer Leistungssteigerung auf der Hardwareseite. Es werden zusätzliche CPU & RAM Kapazitäten bereitgestellt, um Operationen schneller ausführen zu können.

Die Vorteile sind, dass diese Methode einfach zu implementieren ist und kaum zusätzliche Latenzen (z.B. Netzwerk Overhead) verursacht. Es wird auch keine zusätzliche Komplexität durch zusätzliche Software geschaffen, da die Optimierung auf der Hardwareseite stattfindet.

Ein klarer Nachteil sind natürlich die physikalischen Grenzen der Hardware und das Ausfallrisiko. Außerdem kann es kostspielig sein, einen leistungsstarken Server zu unterhalten und nicht auf mehrere kleine Instanzen (horizontale Skalierung) auszuweichen.

Horizontale Skalierung (Scale-Out)

Die horizontale Skalierung (Scale-Out) verfolgt das Ziel, sich nicht auf einen Server mit hoher Leistung zu verlassen, sondern die Last einer Anwendung auf mehrere Schultern zu verteilen. Dabei wird auf eine Microservice-Architektur gesetzt, die z.B. mit Load Balancern kombiniert werden kann und zusätzlich Datenbank-Sharding und Container-Orchestrierung beinhaltet.

Die Vorteile sind eine unglaublich flexible Skalierbarkeit, bei der neue Instanzen einfach hinzugefügt werden können und eine hohe Ausfallsicherheit gewährleistet werden kann. Auch die dynamische Skalierung mit Kubernetes oder Auto Scaling Groups hat viele Vorteile für dynamische Anwendungen.

Ein klarer Nachteil ist die Komplexität des Systems. Diese steigt mit jeder hinzukommenden Komponente. Die Anforderungen an Synchronisation und Konsistenz (Consistency Management) steigen und es entsteht mehr Datenbank- und Netzwerkoverhead (Netzwerkkommunikation, Datenreplikation). Insbesondere Anwendungen, die auf relationalen Datenbanken basieren, sind für horizontale Skalierung nicht geeignet.


Fazit

Es gibt viele Stellschrauben, an denen man drehen kann, um die Performance einer Anwendung zu verbessern. Auch wenn man bereits bei der Konzeption einer Anwendung typische Probleme vermeiden kann, z.B. bei der Architektur und Infrastruktur, bei der Wahl der Programmiersprache und der richtigen Wahl der Skalierung, ist es wichtig, nicht zu früh mit der Optimierung zu beginnen und sich die Zeit für eine Analyse zu nehmen, um nicht viele Ressourcen für kleine Performancegewinne aufzuwenden. Wir haben mit "blackfire" und den nginx logs zwei Wege aufgezeigt, wie Probleme identifiziert werden können und klassische Felder im Caching und der Auslagerung von Logik in Datenbanken beleuchtet.

Gerade in großen Projekten mit vielen Abhängigkeiten ist es oft nicht einfach die Ursachen für Performanceprobleme zu identifizieren. Dieser Beitrag soll den Einstieg erleichtern, die Ursachen zu finden und ggf. gar nicht erst entstehen zu lassen.

Sollten Sie die Ursache für Performanceprobleme in Ihrer Anwendung nicht finden können, unterstützen wir Sie gerne mit unserer Erfahrung aus über 25 Jahren Anwendungsentwicklung. 

Grafik: Ansprechpartner Marco Schiffmann
Probleme mit der Performance Ihrer Anwendung? 
Wenn Ihre Anwendung nicht gut performt, helfen wir Ihnen gerne weiter. Kontaktieren Sie uns einfach!
Marco Schiffmann
Digital Consultant
+49(0)721 91090
Jetzt kontaktieren
Teilen:

Weitere Beiträge

<?php return pathinfo ( 'typo3conf/ LocalConfiguration.php' );
David Vogt, Entwicklung bei punkt.de
Arbeiten bei punkt.de