Ich plane einen verzögerten Job, um Hintergrundanalysen durchzuführen. In meinem ersten Test habe ich enorme Speicherauslastung gesehen, also habe ich im Grunde eine sehr einfache Aufgabe erstellt, die alle 2 Minuten ausgeführt wird, nur um zu beobachten, wie viel Speicher verwendet wird.
Die Aufgabe ist sehr einfach und die analytics_eligbile? Methode gibt immer false zurück, wo die Daten jetzt sind, also wird im Grunde keiner der schweren Treffer aufgerufen. Ich habe rund 200 Posts in meinen Beispieldaten in Entwicklung. Posten Sie has_one analytics_facet.
Unabhängig von der internen Logik / dem Geschäft hier ist das einzige, was diese Aufgabe macht, den analytics_eligible aufzurufen? Methode 200 mal alle 2 Minuten. Innerhalb von 4 Stunden liegt die physische Speicherauslastung bei 110 MB und der virtuelle Speicher bei 200 MB. Nur um etwas so einfach zu machen! Ich kann mir nicht einmal vorstellen, wie viel Speicher das kostet, wenn es echte Analysen auf 10.000 Posts mit echten Produktionsdaten macht !! Zugegeben, es läuft vielleicht nicht alle 2 Minuten, mehr wie alle 30, aber ich glaube nicht, dass es fliegen wird.
Hier läuft ruby 1.9.7, rails 2.3.5 unter Ubuntu 10.x 64 bit. Mein Laptop hat 4 GB Arbeitsspeicher, Dual-Core-CPU.
Sind Schienen wirklich so schlecht oder mache ich etwas falsch?
%Vor%ActiveRecord ist ziemlich speicherintensiv - seien Sie beim Selektieren sehr vorsichtig und achten Sie darauf, dass Ruby automatisch die letzte Anweisung in einem Block als Rückgabewert zurückgibt. Dies bedeutet möglicherweise, dass Sie ein Array von gespeicherten Daten zurückgeben als Ergebnis irgendwo und somit nicht für GC in Frage kommen.
Wenn Sie zusätzlich "Post.not_expired.each" aufrufen, laden Sie all Ihre nicht abgelaufenen Beiträge in den Arbeitsspeicher. Eine bessere Lösung ist find_in_batches, die jeweils nur X Datensätze gleichzeitig in den Arbeitsspeicher lädt.
Es könnte etwas so einfaches sein wie:
%Vor% Hier geschehen ein paar Dinge. Zuerst wird das Ganze in einer Funktion behandelt, um zu verhindern, dass variable Kollisionen Referenzen von den Blockiteratoren halten. Als Nächstes ruft find_in_batches batch_size
-Objekte gleichzeitig aus der Datenbank ab. Solange Sie keine Verweise darauf erstellen, werden Sie nach jeder Iteration für die Garbage Collection qualifiziert, wodurch die gesamte Speicherauslastung niedrig bleibt. Schließlich rufen wir GC.start
am Ende der Methode auf; Dies zwingt den GC, einen Sweep zu starten (was Sie in einer Echtzeit-App nicht tun möchten, aber da dies ein Hintergrundjob ist, ist es in Ordnung, wenn zusätzliche 300 ms benötigt werden). Es hat auch den eindeutigen Vorteil, wenn nil
zurückgegeben wird, was bedeutet, dass das Ergebnis der Methode nil
ist, was bedeutet, dass wir uns nicht versehentlich an AR-Instanzen halten können, die vom Finder zurückgegeben werden.
Wenn Sie so etwas verwenden, sollten Sie sicherstellen, dass keine durchgesickerten AR-Objekte vorhanden sind. Dies sollte sowohl die Leistung als auch die Speichernutzung erheblich verbessern. Sie sollten sicherstellen, dass Sie an keiner anderen Stelle in Ihrer App eindringen (Klassenvariablen, Globals und Klassenreferenzen sind die schlimmsten Täter), aber ich vermute, dass dies Ihr Problem löst.
Alles, was gesagt wurde, das ist ein Cron Problem (periodische wiederkehrende Arbeit), eher als ein DJ Problem, meiner Meinung nach. Sie können einen One-Shot-Analyse-Parser verwenden, der Ihre Analysen alle X Minuten mit script/runner
, aufgerufen von cron, ausführt. Dadurch werden mögliche Speicherlecks oder -missbräuche pro Durchlauf (da der gesamte Prozess am Ende beendet wird) sehr sauber bereinigt / p>
Das Laden von Daten in Batches und die aggressive Verwendung des Garbage Collectors, wie Chris Heald vorgeschlagen hat, wird Ihnen einige wirklich große Vorteile bringen, aber ein anderer Bereich, den Leute oft übersehen, ist, in welche Frameworks sie geladen werden.
Wenn Sie einen Standard-Rails-Stack laden, erhalten Sie ActionController, ActionMailer, ActiveRecord und ActiveResource zusammen. Wenn Sie eine Webanwendung erstellen, verwenden Sie möglicherweise nicht alle diese, aber Sie verwenden wahrscheinlich am meisten.
Wenn Sie einen Hintergrundjob erstellen, können Sie das Laden von nicht benötigten Objekten vermeiden, indem Sie eine benutzerdefinierte Umgebung dafür erstellen:
%Vor% Jedes dieser Frameworks wird nur herumstehen und auf eine E-Mail warten, die niemals gesendet wird, oder auf einen Controller, der nie aufgerufen wird. Es hat einfach keinen Sinn, sie zu laden. Passen Sie Ihre database.yml
-Datei an, legen Sie Ihren Hintergrundjob so fest, dass er in der production_bg
-Umgebung ausgeführt wird, und Sie haben eine viel sauberere Slate, mit der Sie beginnen können.
Sie können ActiveRecord auch direkt verwenden, ohne Rails zu laden. Dies ist möglicherweise alles, was Sie für diese bestimmte Operation benötigen. Ich habe auch festgestellt, dass die Verwendung eines leichten ORM wie Sequel Ihren Hintergrundjob sehr leicht macht, wenn Sie es tun meist SQL-Aufrufe zum Reorganisieren von Datensätzen oder zum Löschen alter Daten. Wenn Sie Zugriff auf Ihre Modelle und deren Methoden benötigen, müssen Sie ActiveRecord jedoch verwenden. Manchmal lohnt es sich jedoch, aus Gründen der Leistung und Effizienz einfache Logik in reinem SQL zu implementieren.
Bei der Messung der Speicherbelegung ist nur der "echte" Speicher von Bedeutung. Die virtuelle Menge enthält gemeinsam genutzte Bibliotheken, deren Kosten unter jedem Prozess verteilt werden, der sie verwendet, obwohl sie für jede einzelne vollständig gezählt wird.
Wenn am Ende etwas Wichtiges 100 MB Speicher benötigt, aber Sie es mit drei Wochen Arbeit auf 10 MB bringen können, sehe ich nicht, warum Sie sich darum kümmern. 90MB Speicher kosten höchstens 60 $ / Jahr bei einem Managed Provider, der in der Regel weit weniger teuer ist als Ihre Zeit.
Ruby on Rails umfasst die Philosophie, sich mehr mit Ihrer Produktivität und Ihrer Zeit zu beschäftigen als mit der Speichernutzung. Wenn Sie es zurückschneiden möchten, setzen Sie es auf eine Diät, Sie können es tun, aber es wird ein bisschen Anstrengung brauchen.
Wenn Sie Probleme mit dem Arbeitsspeicher haben, können Sie eine andere Hintergrundverarbeitungstechnologie verwenden, z. B. resque . Es ist die BG-Verarbeitung, die von github verwendet wird.
Danke an Resques Eltern / Kind Architektur, Jobs, die zu viel verbrauchen Erinnerung befreit diese Erinnerung Fertigstellung. Kein unerwünschtes Wachstum
Wie?
Auf bestimmten Plattformen, wenn ein Resque Arbeiter reserviert einen Job sofort gabelt einen Kindprozess. Das Kind verarbeitet den Job und wird beendet. Wenn das Kind hat erfolgreich verlassen, die Arbeiter reserviert einen anderen Job und wiederholt den Vorgang.
Sie können weitere technische Details in der README finden.
Fakt ist, dass Ruby Speicher verbraucht (und leckt). Ich weiß nicht, ob Sie viel dagegen tun können, aber ich empfehle Ihnen zumindest, sich die Ruby Enterprise Edition anzuschauen / p>
REE ist ein Open-Source-Port, der unter all den anderen guten Dingen "33% weniger Speicher" verspricht. Ich habe REE mit Passenger seit fast zwei Jahren in der Produktion eingesetzt und bin sehr zufrieden.
Tags und Links ruby-on-rails performance delayed-job