96 Punkte von GN⁺ 2025-08-18 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Gutes Systemdesign ist eine Form, die nicht komplex wirkt und über lange Zeit keine nennenswerten Probleme verursacht
  • Der Umgang mit Zustand (state) ist der schwierigste Teil beim Systemdesign, und wichtig ist, die Zahl der Komponenten, die Zustand speichern, möglichst klein zu halten
  • Die Datenbank ist meist der Ort, an dem Zustand gespeichert wird; daher ist ein Ansatz nötig, der sich auf Schema-Design und Indizierung sowie auf die Beseitigung von Engpässen konzentriert
  • Caching, Event-Verarbeitung und Hintergrundaufgaben sollten für Performance und Wartbarkeit mit Bedacht eingeführt werden; übermäßiger Einsatz ist zu vermeiden
  • Statt komplexer Entwürfe ist der angemessene Einsatz hinreichend bewährter einfacher Komponenten und Methoden entscheidend für den Aufbau nachhaltiger und stabiler Systeme

Definition von Systemdesign und allgemeiner Ansatz

  • Wenn Softwaredesign das Zusammensetzen von Code ist, dann ist Systemdesign der Prozess, verschiedene Services zu kombinieren
  • Zu den wichtigsten Bestandteilen des Systemdesigns gehören App-Server, Datenbanken, Caches, Queues, Event-Busse und Proxys
  • Gutes Design ruft Reaktionen hervor wie „es gibt keine besonderen Probleme“, „es war einfacher als gedacht erledigt“ oder „darum muss man sich nicht kümmern“
  • Umgekehrt kann ein komplexes und auffälliges Design grundlegende Probleme verdecken oder auf Overengineering hinweisen
  • Statt komplexe Systeme von Anfang an direkt einzuführen, ist es vorteilhafter, von einer minimal funktionsfähigen einfachen Struktur aus schrittweise weiterzuentwickeln

Unterscheidung zwischen Zustand (state) und Stateless

  • Der schwierigste Teil im Softwaredesign ist das Zustandsmanagement
  • Services, die keine Informationen speichern und sofort ein Ergebnis zurückgeben (wie das PDF-Rendering von GitHub), sind stateless
  • Dagegen verwalten Services, die Schreibvorgänge in einer Datenbank durchführen, Zustand
  • Es ist gut, die Zahl der zustandsspeichernden Komponenten im System so weit wie möglich zu reduzieren. Das senkt die Komplexität des Systems und die Wahrscheinlichkeit von Ausfällen
  • Empfehlenswert ist eine Struktur, in der nur ein Service das Zustandsmanagement übernimmt und die übrigen Services sich auf stateless Rollen wie API-Aufrufe oder das Auslösen von Events konzentrieren

Datenbankdesign und Engpässe

Schema- und Index-Design

  • Für die Datenspeicherung ist ein für Menschen gut lesbares Schema-Design erforderlich
  • Ein zu flexibles Schema (z. B. alles in einer JSON-Spalte zu speichern) kann Anwendungscode und Performance belasten
  • Für Spalten, auf denen häufig Queries ausgeführt werden, sollten geeignete Indizes gesetzt werden. Alles zu indizieren erzeugt im Gegenteil unnötigen Overhead

Methoden zur Beseitigung von Engpässen

  • Datenbankzugriffe werden oft zu schweren Engpässen
  • Nach Möglichkeit ist es aus Performance-Sicht günstiger, komplexe Daten nicht in der Anwendung, sondern innerhalb der Datenbank mit JOINs usw. zu verarbeiten
  • Bei der Nutzung von ORM sollte man Fehler vermeiden, bei denen innerhalb von Schleifen Queries ausgelöst werden
  • Je nach Bedarf kann es auch eine Methode sein, Queries aufzuteilen, um die Last auf der Datenbank oder die Query-Komplexität zu steuern
  • Die Strategie, Lese-Queries auf Replikate (read replicas) zu verteilen, ist wirksam, um die Last auf dem primären Schreibknoten zu senken
  • Wenn sich große Mengen an Queries stauen, können Transaktionen und Schreiboperationen die Datenbank leicht überlasten; daher sollte Query-Throttling (Begrenzung) erwogen werden

Trennung langsamer und schneller Aufgaben

  • Aufgaben, mit denen Nutzer interagieren, benötigen Antworten innerhalb von einigen hundert Millisekunden
  • Bei zeitaufwendigen Aufgaben (z. B. Umwandlung großer PDFs) ist ein Muster effektiv, bei dem im Frontend nur das Nötigste sofort bereitgestellt und der Rest in den Hintergrund verlagert wird
  • Hintergrundaufgaben arbeiten in der Regel mit einer Queue (z. B. Redis) und einem Job Runner zusammen
  • Für weit im Voraus geplante Aufgaben ist es praktischer, statt Redis eine separate DB-Tabelle zur Verwaltung zu verwenden und die Ausführung über einen Scheduler zu steuern

Caching

  • Caching trägt zur Kostensenkung und Leistungssteigerung bei, wenn dieselben oder teuren Berechnungen wiederholt werden
  • Junior Engineers, die Caching gerade erst gelernt haben, möchten oft alles cachen; erfahrene Engineers sind bei der Einführung von Caches vorsichtiger
  • Ein Cache führt neuen Zustand ein, daher bestehen Risiken wie Synchronisationsprobleme, Fehler und stale Daten
  • Sinnvoll ist es, zuerst Performance-Verbesserungen wie zusätzliche Indizes für Queries zu versuchen und erst danach Caching anzuwenden
  • Für große Caches kann statt Redis/Memcached auch ein Ansatz genutzt werden, bei dem periodisch in einen Objektspeicher wie S3/Azure Blob Storage geschrieben wird

Event-Verarbeitung

  • Die meisten Unternehmen verfügen über einen Event-Hub (z. B. Kafka), und verschiedene Services verarbeiten verteilt auf Event-Basis
  • Statt Events wahllos zu verwenden, ist ein einfaches Request-Response-API-Design für Logging und Problemlösung nützlicher
  • Event-basierte Verarbeitung eignet sich, wenn sich der Sender nicht um das Verhalten des Empfängers kümmern muss oder für Szenarien mit hohem Volumen und tolerierbarer Latenz

Arten der Datenübertragung: Push und Pull

  • Für die Datenübertragung gibt es zwei Methoden: Pull (Anfrage und Antwort) und Push (automatische Zustellung bei Änderungen)
  • Der Pull-Ansatz ist einfach, kann aber zu wiederholten Anfragen und Überlastung führen
  • Beim Push-Ansatz werden Datenänderungen vom Server sofort an den Client übermittelt; das ist effizient und vorteilhaft, um aktuelle Daten zu halten
  • Für die Verarbeitung großer Client-Zahlen muss die Infrastruktur je nach Methode entsprechend ausgebaut werden (Event-Queues, mehrere Cache-Server usw.)

Fokus auf Hot Paths

  • Hot Paths bezeichnen die wichtigsten Pfade im System, durch die viele Daten fließen
  • Hot Paths lassen wenig Spielraum; bei Designfehlern können sie schwerwiegende Probleme für den gesamten Service verursachen, daher ist sorgfältiges Design unverzichtbar
  • Statt Ressourcen auf weniger wichtige Funktionen mit vielen Optionen zu verteilen, ist es effektiver, sich auf Hot Paths zu konzentrieren und dort Design und Tests zu priorisieren

Logging, Metriken, Tracing

  • Um bei Störungen die Ursache zu diagnostizieren, sollten detaillierte Logs für ungewöhnliche Fehlerpfade (unhappy path) aktiv aufgezeichnet werden
  • Es ist notwendig, grundlegende Observability-Metriken zu erfassen, etwa Systemressourcen (CPU/Speicher), Queue-Größen sowie Request- und Job-Laufzeiten
  • Statt nur auf Durchschnittswerte zu schauen, müssen auch Verteilungsmetriken wie p95- und p99-Latenzen unbedingt beobachtet werden. Ein kleiner Anteil besonders langsamer Requests kann das eigentliche Problem wichtiger Nutzer sein

Kill Switch, Retries und Wiederherstellung bei Ausfällen

  • Die strategische Nutzung von Kill Switches (temporäre Systemabschaltung) und Retries ist wichtig
  • Wahllose Retries belasten nur andere Services; wirksam werden sie erst, wenn Requests im Vorfeld etwa mit einem Circuit Breaker kontrolliert werden
  • Durch die Einführung eines Idempotency Key können bei der Wiederverarbeitung identischer Requests doppelte Arbeiten verhindert werden
  • In manchen Störungssituationen ist eine Entscheidung zwischen fail open und fail closed nötig. Beispielsweise ist beim Rate Limiting fail open (zulassen) die Variante mit geringerer Auswirkung auf Nutzer. Dagegen ist bei Authentifizierung fail closed zwingend

Abschluss

  • Einige Themen wie Service-Trennung, Container, die Einführung von VMs und Tracing wurden ausgelassen, doch der Einsatz gut bewährter Komponenten an der richtigen Stelle führt langfristig zum stabilsten Systemaufbau
  • Technisch besonders ausgefallene Entwürfe sind in der Praxis sehr selten; so einfache Entwürfe, dass sie fast langweilig wirken, werden im Arbeitsalltag am häufigsten verwendet
  • Im Kern ist gutes Systemdesign ein Prozess, bei dem unauffällige, ausreichend bewährte Methoden sicher miteinander kombiniert werden

Noch keine Kommentare.

Noch keine Kommentare.