Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Performance-Optimierung

Performance-Optimierung

~17 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Bevor wir in Kapitel 48 deployen, ein systematischer Blick auf Performance – insbesondere das N+1-Query-Problem, eine der häufigsten Ursachen für langsame Symfony/Doctrine-Anwendungen.

Den Symfony Profiler nutzen

In der dev-Umgebung (Kapitel 4) zeigt die Debug-Toolbar am unteren Bildschirmrand JEDE Anfrage im Überblick – ein Klick öffnet den vollständigen Profiler mit Details zu:

  • Gesamt-Ausführungszeit UND Aufschlüsselung nach Phase (Routing, Controller, Rendering).
  • ALLEN ausgeführten Datenbank-Queries, inklusive tatsächlicher Ausführungszeit JEDER einzelnen Query.
  • Speicherverbrauch.
  • Geladenen Services UND deren Erzeugungsreihenfolge.

Das N+1-Query-Problem erkennen

Ein KLASSISCHES Performance-Problem, demonstriert an einer Projektliste mit Aufgaben-Anzahl:

{% for projekt in projekte %}
    <li>{{ projekt.name }} ({{ projekt.tasks|length }} Aufgaben)</li>
{% endfor %}

Achtung: Sieht harmlos aus, ist es aber NICHT: projekt.tasks löst pro Schleifendurchlauf eine EIGENE Datenbank-Query aus (Doctrines "Lazy Loading", Kapitel 22) – bei 50 Projekten sind das EINE Query für die Projektliste PLUS 50 WEITERE Queries für die jeweiligen Aufgaben, statt EINER einzigen, effizienten Abfrage. Der Profiler aus diesem Kapitel macht GENAU dieses Muster als "51 Queries" sofort sichtbar.

Die Lösung: Eager Loading mit JOIN

src/Repository/ProjectRepository.php
/**
 * @return Project[]
 */
public function findAlleMitAufgabenAnzahl(): array
{
    return $this->createQueryBuilder('p')
        ->leftJoin('p.tasks', 't')
        ->addSelect('t')
        ->getQuery()
        ->getResult()
    ;
}

addSelect('t') ist der ENTSCHEIDENDE Unterschied zum reinen leftJoin aus Kapitel 24: OHNE addSelect würde Doctrine den JOIN nur zum FILTERN nutzen, die Task-Objekte selbst aber weiterhin SEPARAT nachladen. MIT addSelect('t') werden Projekte UND ihre Aufgaben in EINER EINZIGEN Query geladen – aus 51 Queries werden 1.

Die Query-Anzahl im Profiler vergleichen

Tipp: Rufen Sie die Projektliste VOR und NACH der Umstellung im Profiler auf und vergleichen Sie die Anzahl der Datenbank-Queries – DIESE messbare Zahl, nicht nur das subjektive "fühlt sich schneller an", ist der verlässliche Nachweis einer echten Verbesserung.

Weitere häufige Performance-Fallen

FalleBeschreibung
Fehlende Datenbank-IndizesEin Feld, nach dem HÄUFIG gefiltert/sortiert wird (z. B. status in Kapitel 24), aber OHNE Index, führt zu langsamen Full-Table-Scans bei wachsender Datenmenge.
Ungenutzte Ergebnisse ladenfindAll() nutzen, wenn nur 10 von 10.000 Zeilen gebraucht werden – IMMER Pagination oder gezielte WHERE-Bedingungen verwenden.
Fehlendes CachingKapitel 45/46 wiederholt berechnete/abgefragte Werte NICHT nutzen, obwohl sie sich SELTEN ändern.

Einen fehlenden Index ergänzen

#[ORM\Entity(repositoryClass: TaskRepository::class)]
#[ORM\Index(columns: ['status'], name: 'idx_task_status')]
class Task
{
    // ...
}
php bin/console make:migration
php bin/console doctrine:migrations:migrate

GENAU derselbe Workflow aus Kapitel 20 – ein Index ist letztlich eine Datenbank-Struktur-Änderung wie jede andere, versioniert per Migration.