[TYPO3-german] Ist Extbase ein Speicher-Fresser und Performance-Killer?

Stephan Schuler Stephan.Schuler at netlogix.de
Mon Oct 31 12:01:25 CET 2016


Hallo Alexander.

Du mischst in Deinem Beispiel zwei aus Sicht der Datenstruktur ziemlich unterschiedliche Situationen. Die eine lässt sich optimieren (und wird imho auch von Extbase entsprechend optimiert behandelt), die zweite „erst mal nicht“ rein innerhalb des Frameworks. Trotzdem kannst Du da natürlich ein optimiertes Model verwenden.

Deshalb kann man erst mal pauschal sagen: Ja, wenn Du nichts über die internen Zusammenhänge weißt kostet dich ein ORM immer Ressourcen. Aber dafür hast Du eben auch einen ORM und damit Zugriff auf dein logisches Datenmodel der im Idealfall exakt mit dem übereinstimmt, was sich „die Fachabteilung“ darunter vorstellt.

Variante 1:

Grundsätzlich frisst das Pagination-Widget ein QueryResult-Objekt. Das hat die Eigenschaft, dass es erst mal nur das Query-Statement (in Form Query-Objects, das wiederum die gewünschten Datenmengen in Form von QOM-Constraints abbildet) enthält, nicht aber alle abgefragten Daten. Alle angefragten Daten werden erst aus der Datenbank in das QueryResult-Objekt geladen, wenn „eine Methode gerufen wird, die es initialisiert“. Das sind vorwiegend alle Methoden die durch das Iterator-Interface oder das ArrayAccess-Interface vorgegeben sind.

Wenn das Pagination-Widget auf „eine Seite“ zugreifen will, wendet es auf das Query-Objekt innerhalb des QueryResult-Objekts die Operationen setLimit() und setOffset() an. Dadurch verringert sich natürlich die Datenmenge erheblich, auf genau die Daten die Du haben möchtest.

Ich möchte nicht ausschließen, dass Du trotzdem irgendwie erst mal alle Daten in das Objekt lädst. Zwar sind diverse ViewHelper entsprechend schlau. Den PaginateViewHelper habe ich ja bereits erklärt. Aber z.B. auch der CountViewHelper läd natürlich nicht alles aus der Datenbank und zählt dann, sondern er wendet count() auf das QueryResult-Objekt an, und das wiederum hängt ein SQL-Count an das Statement und fragt die Datenbank nach der Anzahl der Daten. Trotzdem gibt es natürlich eine Fülle an Operationen, die trotzdem den kompletten Datenbestand laden. Und wenn Du eine davon triggerst explodiert Dir der Arbeitsspeicher.

https://typo3.org/api/typo3cms/_widget_2_controller_2_paginate_controller_8php_source.html#l00091
https://typo3.org/api/typo3cms/_widget_2_controller_2_paginate_controller_8php_source.html#l00180
https://typo3.org/api/typo3cms/_query_result_8php_source.html#l00130

Das ist jetzt der erste Fall: Zugriff auf eine Datenmenge die sich durch einen Query definiert.

Variante 2:

Der zweite Fall ist der Zugriff auf Relationen. Für die gibt es keinen Query sondern die hält Extbase in einem ObjectStorage-Objekt vor. Und das läd sich entweder ganz oder gar nicht. Auch hier gilt mein Link zur Methode prepareObjectSlice des PaginateControllers, allerdings nicht das erste if das den Query manipuliert sondern das zweite, das mit einer for-Schleife Daten aus dem ObjectStorage-Objekt in ein Array kopiert.

Tada, Speicherproblem.

Und das gleiche Verhalten hast Du natürlich auch, wenn Du in Deinem eigenen Code „toArray“ auf ein QueryResult anwendest.

Du könntest aber, zumindest für den lesenden Zugriff, die Variante 2 in die Variante 1 überführen.

Ich verwende mal die Beziehung „one car has many passengers“. Mal sehen ob das ein sinnvolles Beispiel ergibt.

„Laut Lehrbuch“ hast du eine Relation-Property „protected $passengers;“ im Mutterobjekt „Car“, die ist vom Typ ObjectStorage, und da sind die Relationen „Passenger“ drin. Und Du hast die zugehörige Getter-Methode „public getIrgendwas()“ die diese Property ausgibt, die hat als Rückgabe die Signatur „ObjectStorage<Passenger>“.

Dein Datenmodell hat also ein Aggregate-Root „Car“ und diverse „Passenger“-Objekte als Child-Nodes des Mutterobjekts. Wenn wir jetzt mal Joins weglassen (die sind im ORM immer irgendwie schwierig, weil sie zwar aus Sicht der Mengenlehre sinnvoll sind, aber aus Sicht der Objekt-Datenstruktur Quark ergeben weil sie Objekte verschmelzen), müsstest Du in einem SQL-Query etwa „SELECT * FROM passenger WHERE FIND_IN_SET(passenger.uid, SELECT passengers from car WHERE uid = %CarId)“ schreiben. Wie gesagt, die Datenstruktur ist ineffizient und ich würde mal auf eine 80%-Chance tippen dass der Exbase-Kickstarter die so nicht anlegt, aber das gedankliche Modell der oben genannten Beziehung sieht für mich erst mal so aus wie mein Query.

Wenn Du auch diese Passenger-Objekte als Aggregate-Roots darstellst und sie auf das Car-Objekt beziehen lässt, kannst Du einen Query bauen der das abbildet und deutlich einfacher ist.

Anstatt also „car [has many] passenger“ zu schreiben, könntest Du auch (zusätzlich oder alternativ, je nachdem von welcher Seite Du auf die Daten zugreifen willst) „passenger [has one] car“ schreiben.
Und schon kannst Du eine Methode „Car::getPassengers()“ bauen die „return $this->passengerRepository->findAllByCar($this);“ zurückgibt – und das ist jetzt kein StorageObject mehr sondern ein QueryResult und damit per Limit/Offset paginierbar.

Natürlich ist das jetzt sehr vereinfacht. Ich habe nicht über Caching gesprochen (bei Mehrfachzugriff auf diese Methode) und noch nicht über den PropertyMapper (der ins Spiel kommt, wenn Du eine Methode „CarController::createAction()“ hast die ein Car mit Passengers anlegen soll). Aber das ist ja vielleicht auch nicht immer notwendig.

Beste Grüße,

Am 30.10.16, 10:26 schrieb "typo3-german-bounces at lists.typo3.org im Auftrag von Alexander Averbuch" <typo3-german-bounces at lists.typo3.org im Auftrag von alav at gmx.net>:

    Hallo zusammen,

    korrigiert mich, wenn ich unrecht habe.

    Ich nutze das Pagination Widget von Extbase. Es funktioniert wunderbar, nur erwartet das Widget als Parameter immer ALLE gefundenen Objekte und entscheidet, welche davon angezeigt werden müssen. Sprich, wenn ich 1 Mio von Objekten in der DB habe, werden sie alle in den Speicher geladen.
    Erstens belastet es stark den MySQL-Server, was zu Verzögerungen führen kann, zweitens wie groß soll dann der Server-Speicher sein???
    Oder geht man davon aus, dass solche großen Datenmengen nicht mit der Paginierung vereinbar sind?

    Wenn ich ein Model mir Relations habe, werden alle Relations-Objekte mitgeladen. @lazy hilft hier wenig um Speicher zu sparen, denn beim ersten Aufruf von Relation-Objekten werden sie ALLE in den Speicher geladen.

    Gruß,

    Alexander

Stephan Schuler
Web-Entwickler | netlogix Web Solutions

Telefon: +49 (911) 539909 - 0
E-Mail: Stephan.Schuler at netlogix.de
Web: websolutions.netlogix.de



----------------------------
Web Solutions News
Einblicke in unsere Arbeit. https://websolutions.netlogix.de/referenzen
Jobs in unserem Team. https://websolutions.netlogix.de/jobs
----------------------------




netlogix GmbH & Co. KG
IT-Services | IT-Training | Web Solutions
Neuwieder Straße 10 | 90411 Nürnberg
Telefon: +49 (911) 539909 - 0 | Fax: +49 (911) 539909 - 99
E-Mail: info at netlogix.de | Web: http://www.netlogix.de

netlogix GmbH & Co. KG ist eingetragen am Amtsgericht Nürnberg (HRA 13338)
Persönlich haftende Gesellschafterin: netlogix Verwaltungs GmbH (HRB 20634)
Umsatzsteuer-Identifikationsnummer: DE 233472254
Geschäftsführer: Matthias Schmidt



_______________________________________________
    TYPO3-german mailing list
    TYPO3-german at lists.typo3.org
    http://lists.typo3.org/cgi-bin/mailman/listinfo/typo3-german



More information about the TYPO3-german mailing list