Was ist das N+1 Query Problem?

Beim “N+1 Query Problem” handelt es sich um ein “Performance Antipattern” bezüglich Datenbankabfragen.

Das Problem lässt sich am besten an einem Beispiel erläutern:

Angenommen, man hat in der Datenbank unter anderem Blog Posts und Autoren als Entities gespeichert und möchte auf einer Seite für alle Beiträge das Blog-Post-Datum, den Blog-Post-Titel und den Namen des Blog-Post-Autors anzeigen.

Insbesondere wenn man einen “Object Relational Mapper” (ORM) wie Doctrine verwendet, kann es dann leicht passieren, dass man auf das Performance-Problem der N+1 Queries stößt:

Man erstellt zunächst 1 Query (dies ist die Abfrage “plus eins”), um die Daten aller Blog-Post-Entities abzuholen (“SELECT date, title, author_id FROM blog_posts”) und dann für alle Blog Posts (“N”) jeweils eine weitere Query, um den Namen des zugehörigen Autors abzufragen (“SELECT name FROM authors WHERE id = ?”).

Das Problem tritt auf, wenn man das sogenannte “lazy loading/fetching” (“faules Laden”) benutzt, bei dem Daten erst dann abgefragt werden, wenn sie benötigt werden. Für Doctrine von dessen Entwickler Benjamin Eberlei hier angesprochen: https://tideways.io/profiler/blog/5-doctrine-orm-performance-traps-you-should-avoid

Lösung ist das “eager loading/fetching” (“eifriges Laden”), das von vornherein alle Daten mittels einer einzigen (zusätzlichen) Datenbankabfrage lädt.

Doctrine lädt Accociations grundsätzlich “lazy” (“Associations are marked as Lazy by default.“).

Das bedeutet, dass Doctrine an den Stellen, wo normalerweise assoziierte Objekte stehen würden, zunächst Proxy Objects einfügt.

The second most important situation where Doctrine uses proxy objects is when querying for objects. Whenever you query for an object that has a single-valued association to another object that is configured LAZY, without joining that association in the same query, Doctrine puts proxy objects in place where normally the associated object would be. Just like other proxies it will transparently initialize itself on first access.

https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/advanced-configuration.html#association-proxies