Mögliche Lösungen zur Verlangsamung von Left-Joins in komplexen Doktrinabfragen
<p>Ich habe eine Symfony-Repository-Methode, die einen ziemlich komplexen Datensatz abruft, der dann von einer Export-Manager-Klasse in einer CSV-Datei abgelegt wird. Ich möchte nicht den gesamten Code einfügen, der den Exportauftrag abwickelt, aber es ist mir gelungen, den Punkt zu bestimmen, an dem die Abfrage langsamer wird. Daher bezieht sich meine Frage auf andere Alternativen, um die Abfrage schneller zu machen, und nicht auf den Code selbst.
Bei den abgerufenen Daten handelt es sich also um einige „Site“-Daten, die über mehrere „Mitgliedschaften“ und dann über „Benutzer“ verfügen.Das Problem besteht also darin, dass meine Abfrage, wenn sie versucht, die Benutzerinformationen mit der Site zu verbinden, die Ausführung verlangsamt. Es sieht so aus:</p>
<pre class="brush:php;toolbar:false;">$qb->leftJoin('s.memberships', 'ex_sm', 'WITH', 'ex_sm.revokedAt IS NULL');
$qb->leftJoin('ex_sm.user', 'ex_jappr', 'WITH', 'ex_sm.approverJobReactiveWeight IS NOT NULL');</pre>
<p>Ein paar Dinge, die ich erwähnen sollte (von denen ich versucht habe oder von denen ich dachte, dass sie helfen könnten): </p>
<ul>
<li>Ich habe die Tabelle überprüft und festgestellt, dass alle verknüpften Spalten einen Index haben und alle vom gleichen int-Datentyp sind. </li>
<li>Ich habe einen Artikel über DQL-Leistungsprobleme gelesen, in dem erwähnt wurde, dass eine übermäßige Verwendung von DQL Left Join-Aufrufen die Leistung beeinträchtigen kann, weil sie dasselbe Entitätsobjekt immer wieder neu zuordnen. Eine mögliche mögliche Lösung besteht darin, den Hauptdatensatz abzurufen und dann die Sammlung zu durchlaufen und jedem Element Anhänge (verbundene Datenfelder) direkt aus der Entitätsklasse des Felds hinzuzufügen. Das könnte funktionieren (ich bin mir nicht sicher, wie groß die Auswirkungen wären). Das Problem ist, dass ich sehr komplexen Legacy-Code habe und ich die Logik des Exportmanagers nicht berühren möchte, da dies zu viele Tests erfordern würde. Der Exportmanager benötigt eine Abfrage-Builder-Klasse, daher muss ich die Lösung in der Abfrage selbst finden. </li>
<li>Das Problem wird definitiv durch den Join verursacht, nicht durch die WITH-Klausel oder zusätzliche Bedingungen. Ich habe versucht, die Abfrage mit einem normalen leftJoin-Aufruf aufzurufen, mit dem gleichen Ergebnis. </li>
<li>Ich weiß, dass die Aufrufe der leftJoin-Methode miteinander verkettet werden können. Der Code sieht so aus, weil einige der Aufrufe in if-Anweisungen verwendet werden. </li>
<li>Ich habe zwei Tage lang alles ausprobiert, was ich hier und auf anderen Websites gefunden habe. </li>
</ul>
<p>Es gibt 6 verschiedene Benutzertypen. Jetzt rufe ich einfach das Skript auf, um den oben genannten Benutzertyp abzurufen, und es dauerte 33 Minuten, bis die Daten zurückgegeben wurden. Wir sprechen von 512 Websites, was keine große Datensammlung darstellt.Meine Frage lautet also: Gibt es eine andere DQL- oder Doctrine-Möglichkeit, die Anzahl der Aufrufe von leftJoins in einer so komplexen Abfrage zu vereinfachen oder zu reduzieren und die Leistung irgendwie zu verbessern? </p>
<p>Update:
Ich dachte, das Problem läge beim Index, also habe ich einige Details zur Beziehung angegeben:
Die Entität „Mitgliedschaften“ stammt aus einer Tabelle mit dem Namen „Zugriff“ und die Beziehung zu Benutzern in ihrem Modell ist wie folgt: </p>
<pre class="brush:php;toolbar:false;">/*** Der Benutzer, den diese Mitgliedschaft umfasst.
*
* @ORMManyToOne(targetEntity="User", inversedBy="siteMemberships", cascade={"persist"})
* @ORMJoinColumn(name="security_identity_id", referencedColumnName="id")
*
* @var Benutzer*/
protected $user;</pre>
<p>Dies ist ein Screenshot des Index, der der Spalte „security_identity_id“ zugewiesen ist
</p>
<p>Die zugehörigen Benutzer stammen aus der Tabelle „Benutzer“ mit einer Beziehung, die auf die Mitgliedschaft verweist.</p>
<pre class="brush:php;toolbar:false;">/*** @ORMOOneToMany(targetEntity="SiteMembership", mappedBy="user", cascade={"persist"}, fetch="EXTRA_LAZY")*/
protected $siteMemberships;</pre>
<p>Der Primärschlüssel ist die „id“ in der Entität.Ich hoffe, das bringt die Dinge in eine bessere Perspektive. Ich bin kein SQL-Experte, habe aber alles versucht, was ich gefunden habe, und kann es bisher herausfinden. </p>
<p>Update:
Dies ist die ausgeführte Abfrage: </p>
<pre class="brush:php;toolbar:false;">SELECT s0_.name AS name_0, s0_.id AS id_1, GROUP_CONCAT(DISTINCT u1_.name SEPARATOR ', ') AS sclr_2 FROM site s0_
LEFT JOIN Zugriff auf a2_ ON s0_.id = a2_.entity_id
AND a2_.type IN ('site_member')
UND (a2_.revoked_at IST NULL)
LEFT JOIN Benutzer u1_ ON a2_.security_identity_id = u1_.id
UND (a2_.approver_job_reactive_weight IST NICHT NULL)</pre>
<p>Dadurch wird der erste Site-Datensatz zusammen mit seiner Mitgliedschaft und seinen Benutzerberechtigungen zurückgegeben. Aber selbst diese Reihe dauert über 2 Minuten. </p>
<p>Hier sind die Informationen zur Tabellenerstellung für den Zugriff auf die Tabelle (Mitgliedsentität)</p>
<pre class="brush:php;toolbar:false;">'CREATE TABLE `access` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`buddy_id` int(11) DEFAULT NULL,
`security_identity_id` int(11) DEFAULT NULL,
`revoked_at` datetime DEFAULT NULL,
`created_at` Datum/Uhrzeit NICHT NULL,
`updated_at` datetime NICHT NULL,
`type` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`approver_job_reactive_weight` int(11) DEFAULT NULL,
`entity_id` int(11) STANDARD NULL,
PRIMÄRSCHLÜSSEL („id“),
EINZIGARTIGER SCHLÜSSEL „access_idx“ („type“, „security_identity_id“, „entity_id“, „buddy_id“),
SCHLÜSSEL `IDX_6692B54395CE8D6` (`buddy_id`),
SCHLÜSSEL `IDX_6692B54DF9183C9` (`security_identity_id`),
SCHLÜSSEL `IDX_6692B5481257D5D` (`entity_id`),
SCHLÜSSEL `idx_revoked_id_approver_type` (`revoked_at`, `entity_id`, `approver_job_reactive_weight`, `approver_job_planned_weight`, `type`),
SCHLÜSSEL `idx_user_site_access` (`revoked_at`, `security_identity_id`, `buddy_id`, `type`),
SCHLÜSSEL `idx_user` (`security_identity_id`),
SCHLÜSSEL `idx_user_id` (`security_identity_id`),
CONSTRAINT `FK_6692B54DF9183C9` FOREIGN KEY (`security_identity_id`) REFERENCES `user` (`id`)
)
ENGINE=InnoDB AUTO_INCREMENT=262441 DEFAULT CHARSET=utf8
COLLATE=utf8_unicode_ci'</pre>
<p>Ich habe einige irrelevante Spalten gelöscht. </p>