Performance und Diagnostik
N+1, ungebremste Listen und fehlende Messpunkte systematisch verbessern.
Workshop-Slice
Dieses Lab zeigt einen konkreten Enterprise-Fehler mit Vorher/Nachher-Code. Die Beispiele sind bewusst fachlich benannt, damit du Architekturentscheidungen und nicht nur Syntax übst.
Lernziel
Du analysierst einen realistischen Performance-Fehler und ersetzt Vermutung durch Messung und gezieltes Refactoring.
Ausgangslage
Eine Order-Liste ist in Produktion langsam. Der Code lädt Orders und Positionen unkontrolliert und liefert beliebig große Listen.
Vorher: problematischer Code
Die API lädt zu viel und erzeugt N+1-Queries.
public List<OrderDto> findAllOrders() {
return orderRepository.findAll().stream()
.map(order -> new OrderDto(
order.getBusinessId(),
order.getItems().stream().map(OrderItemEntity::getSku).toList()))
.toList();
}
Analyse: Was ist daran schlecht?
- Unbegrenzte Ergebnisliste.
- Lazy `items` erzeugen pro Order zusätzliche Queries.
- Keine Projektion für Listenansicht.
- Keine Metrik, um Verbesserung objektiv zu sehen.
Nachher: bessere Lösung
Die Zielversion nutzt Pagination, Query-Projektion und Messpunkte.
public record OrderListProjection(
String orderId,
String customerId,
BigDecimal totalAmount,
String status
) {}
public interface OrderReadRepository extends Repository<OrderEntity, UUID> {
@Query("""
select new com.acme.order.query.OrderListProjection(
o.businessId, o.customerId, o.totalAmount, o.status
)
from OrderEntity o
where o.createdAt >= :from
order by o.createdAt desc
""")
Page<OrderListProjection> findRecentOrders(@Param("from") Instant from, Pageable pageable);
}
@GetMapping("/orders")
public Page<OrderListProjection> recent(
@RequestParam Instant from,
@PageableDefault(size = 50, sort = "createdAt", direction = DESC) Pageable pageable) {
return metrics.timer("order.query.recent").record(() ->
repository.findRecentOrders(from, pageable));
}
Test / Prüfnachweis
Dieser Abschnitt zeigt, wie du die Verbesserung nachweist. Es ist bewusst kein reiner Happy-Path-Test, sondern prüft ein Risiko aus dem Vorher-Teil.
# Lasttest mit begrenzter Ergebnisgröße
hey -z 60s -c 20 'https://order-service.apps.local/orders?from=2026-01-01T00:00:00Z&size=50'
# JFR-Aufzeichnung für Hotspots
jcmd $(pgrep -f order-service) JFR.start name=order-query duration=120s filename=/tmp/order-query.jfr
Typische Fehler
- Performance ohne Messung optimieren.
- Fetch Join überall einsetzen und Pagination zerstören.
- Unbegrenzte Listen im REST-API erlauben.
- JPA SQL-Logging dauerhaft in Produktion aktivieren.
Deep-Learning-Bezug
Die Links führen zum ausführlichen Inhalt; die Deep-Learning-Seite bleibt nur die Lernlandkarte und kopiert den Inhalt nicht doppelt.
Prüfcheckliste
- Endpoint ist paginiert.
- Listenansicht nutzt Projektion.
- Messpunkt vor/nach Änderung vorhanden.
- JFR oder Metrikbeweis ist dokumentiert.