11 Punkte von newcodes7 2026-01-19 | Noch keine Kommentare. | Auf WhatsApp teilen

Projektvorstellung

  • NewCodes ist ein Curated-Service für technische Unternehmensblogs
  • Spring Boot + PostgreSQL-Architektur
  • Implementierung einer Suchbegriff-Autovervollständigung: termbasierte Empfehlungen, Suche mit Zerlegung in Jamo, Initialkonsonanten-Suche, Empfehlungen für Unternehmensseiten

Entdeckung des Performance-Problems

  • In der Tabelle Term hatten sich 110.000 Datensätze angesammelt
  • Die API-Antwortzeit stieg auf über 1000 ms
  • Ziel: Antwort innerhalb von 100 ms

1. Versuch: Indizes hinzufügen (1000 ms → 700 ms)

  • Erstellung eines Optimierungsindex für LIKE-Präfixsuchen mit varchar_pattern_ops
  • Indexerstellung mit der Option CONCURRENTLY ohne Service-Unterbrechung
  • Jeweils Indizes auf die Spalten term, decomposed_term und chosung angewendet

2. Versuch: LOWER-Funktionsindex (700 ms → 110 ms)

  • Problem eines Full Scans durch die Verwendung der Funktion LOWER() erkannt
  • Erstellung eines funktionsbasierten Indexes (Functional Index)
  • Neuaufbau des Indexes in der Form LOWER(Spaltenname) varchar_pattern_ops

3. Versuch: JOIN → EXISTS (110 ms → 100 ms)

  • INNER JOIN zwischen Corporation und Article war ein Performance-Flaschenhals
  • Wechsel zu einer EXISTS-Subquery zur Reduzierung des Scan-Bereichs
  • Optimierung darauf, nur die „Existenz von Daten“ zu prüfen

4. Versuch: Denormalisierung & Covering Index (100 ms → 90 ms)

  • Hinzufügen der Spalte total_frequency, um Aggregationsoperationen zu entfernen
  • Ersetzung von GROUP BY- und SUM-Operationen durch vorab berechnete Werte
  • Reduzierung der I/O-Anzahl durch einen Covering Index
  • Einbeziehung von term und total_frequency in den Index mit der INCLUDE-Klausel

5. Versuch: JDBC Template (90 ms → 80 ms)

  • Entfernung des JPA/Hibernate-Overheads
  • Direkte Ausführung von Queries mit JDBC Template
  • Bei einfachen Leseabfragen ist das Überspringen der ORM-Schicht effektiv

Lösung des Problems mit Nginx Rate Limiting

  • Ursprüngliche Einstellung: Begrenzung auf 2 Anfragen pro Sekunde, burst 10
  • Fehlgeschlagene Anfragen durch 100-ms-Debouncing
  • Verbesserung: Änderung auf 10 Anfragen pro Sekunde, burst 20
  • Änderung des Statuscodes von 444 → 429

Verkleinerung der Antwortdatengröße

  • Entfernen von JSON-Feldnamen, Umstellung auf arraybasierte Antworten
  • Kennzeichnung des Typs per Zahl (0: Corporation, 1: Theme, 2: Term)
  • Reduzierung der Netzwerkübertragungszeit

Parallele Verarbeitung mit CompletableFuture

  • Gleichzeitige, voneinander unabhängige Ausführung der Abfragen für Corporation, Theme und Term
  • Im Vergleich zur sequenziellen Ausführung fällt nur die maximale Antwortzeit an
  • Hinzufügen von ExecutorService und Exception-Handling

Endergebnis

  • Anfangs 1000 ms → final 80 ms (Entwicklungsserver), 40 ms (Produktivserver)
  • Performance-Verbesserung von rund 90 % oder mehr

Wichtige Erkenntnisse

  • Bedeutung einer klaren Problemdefinition und Richtungsfestlegung
  • Balance zwischen dem Einsatz von KI und der Prüfung durch Entwickler
  • Notwendigkeit von Design aus Sicht der Gesamtarchitektur
  • Auswahl von Indextypen: einfache/zusammengesetzte/Covering Indizes
  • Vorsicht vor der Deaktivierung von Indizes bei Funktionsverwendung
  • Verständnis der internen Funktionsweise von JPA
  • Analyse von Query-Ausführungsplänen mit EXPLAIN

Zukünftige Verbesserungsrichtungen

  • Einsatz der Trie-Datenstruktur
  • Caching häufig gesuchter Begriffe
  • Nutzung eines CDN (bei globalem Service)

Noch keine Kommentare.

Noch keine Kommentare.