Datenbank-Engineering
Datenbank- & Eloquent-Engineering
Die Abfragearbeit hinter einer Laravel-Anwendung, die ihrem Schema entwachsen ist - N+1 beseitigen, Indizes aus echten Abfrageplänen, und Migrationen, die keine laufende Tabelle sperren.
Fast jede Laravel-Anwendung, die langsam wird, wird in der Datenbank langsam, und fast jede davon war in der Entwicklung in Ordnung. Eine Seite, die elf Abfragen gegen vierzig Zeilen ausführt, führt elf Abfragen gegen vier Millionen Zeilen genauso aus, und nur eines von beidem ist überlebbar.
Wohin die Zeit tatsächlich geht
Über die Projekte hinweg, die wir durchführen, gruppieren sich die Ursachen zu einer kurzen Liste.
Ein N+1, das nur in der Produktion auftaucht. Die Übersichtsseite lädt ihre Relation vorab. Die Detailseite nicht, weil sie immer nur einen Datensatz lädt – und dann verwendet jemand die Detail-Komponente in einer Schleife wieder. Die Abfragezahl geht von zwei auf zweihundert und nichts im Diff sieht falsch aus.
Eine Relation, die in einem Accessor geladen wird.
getFullAddressAttribute() greift auf $this->country zu, der Accessor wird
beim Serialisieren aufgerufen, und die Anwendung setzt eine Abfrage pro Zeile
ab, während sie aussieht, als formatiere sie eine Zeichenkette.
Ein Index, der existiert und nicht benutzt wird. Eine Spalte in eine Funktion gewickelt, eine implizite Typumwandlung zwischen einer Zeichenkettenspalte und einem Integer-Parameter, ein führendes Platzhalterzeichen bei LIKE. Der Index ist da, EXPLAIN sagt, er wird nicht gelesen, und alle vertrauen dem Schema statt dem Abfrageplan.
Aggregation in PHP. Die Collection-Pipeline ist ausdrucksstark genug, dass
->get()->groupBy()->map() sich besser liest als das SQL, also werden
hunderttausend Zeilen zu Modellen hydriert, um sechs Zahlen zu erzeugen.
Eine Migration, die eine laufende Tabelle gesperrt hat. Einen Index hinzufügen, eine Spalte mit Standardwert hinzufügen oder einen Spaltentyp ändern – auf einer Tabelle, die groß genug ist, dass die Sperre den Anfrage-Timeout überdauert. Das Deployment war erfolgreich; die Seite war unten.
Wie wir arbeiten
Zuerst messen, am echten System. Slow-Query-Logs, die Abfragezahl pro Endpunkt und EXPLAIN auf den Abfragen, die zählen. Kein Profiler auf einem Laptop mit einer befüllten Testdatenbank, der Ihnen verlässlich von einem Problem erzählt, das Sie nicht haben.
Die Ursache beheben, nicht das Symptom. Ein Cache vor einer schlechten Abfrage ist eine Art, die schlechte Abfrage seltener zu bezahlen. Manchmal ist das die richtige Entscheidung und wir sagen das; häufiger will die Abfrage einen Index, einen Join, oder zur Laufzeit gar nicht erst abgesetzt werden.
Die Korrektur dauerhaft machen. Lazy Loading außerhalb der Produktion abgeschaltet, damit das gerade entfernte N+1 nicht still wieder eingeführt werden kann. Eine Zusicherung auf die Abfragezahl für die Endpunkte, die zählen, damit eine künftig entfernte Vorab-Ladung einen Test scheitern lässt statt ein Support-Ticket auszulösen. Beides sind wenige Zeilen und sie machen den Unterschied zwischen einer Korrektur und einer Korrektur, die bleibt.
Schemaarbeit an einem System, das nicht stehenbleiben darf
Das meiste, worum wir gebeten werden, ist keine Abfrage, sondern eine Form: eine Statusspalte, die eine Statustabelle sein sollte, eine polymorphe Relation, die zwei Zeilen unmöglich beschränkbar gemacht hat, ein Auditlog, das ohne Aufbewahrungsregel wächst, eine Tabelle, die drei Aufgaben erfüllt, weil das Aufteilen vor zwei Jahren teuer schien.
Diese Arbeit passiert in Schritten, die einzeln sicher sind:
- Die neue Struktur neben der alten anlegen, ohne dass etwas sie liest.
- In Stapeln befüllen, auf einer Queue, mit einem Fortschritt, der ein Deployment übersteht.
- In beide schreiben, aus der alten lesen und abgleichen, bis der Unterschied null ist.
- Lesen umstellen. Warten. Dann aufhören, die alte zu beschreiben.
- Sie in einem eigenen Release löschen, sobald sie eine Woche lang niemand angefasst hat.
Das sind mehr Schritte als eine einzelne Migration, und es ist der Unterschied zwischen einer Schemaänderung und einer geplanten Ausfallzeit.
Was Sie bekommen
Ein Befunddokument, in dem jedes Problem auf die Abfrage oder die Migration zurückgeführt ist, die es verursacht, und nach Aufwand bemessen; die Korrekturen als prüfbare Pull Requests; die Indexänderungen als Migrationen, die bei Ihrem Datenvolumen sicher laufen; und die CI-Zusicherungen, die die Rückfälle stoppen.
Wo die Antwort strukturell statt eine Abfrageumschreibung ist, sagen die Befunde das und stellen beide Zahlen nebeneinander: was die Änderung kostet und was es kostet, sie im nächsten Jahr zu lassen. Das sollten Sie in der ersten Woche lesen und nicht in einem Abschlussgespräch hören.
Vor dem Start wollen die meisten zwei Dinge lesen: was eine sperrende Schemaänderung auf einer produktiven Tabelle kostet und worin sich MySQL und Postgres für eine Laravel-Anwendung tatsächlich unterscheiden. Stellt sich heraus, dass nicht die Abfrage langsam ist, sondern der Request-Pfad, ist Performance das Nachbarprojekt.
