Schreiben Sie Code in Rails mit Blick auf den Speicher?
Rails verwendet Rubys ** Garbage Collection * 1 **, sodass Sie Code schreiben können, ohne sich Gedanken über die Freigabe von Speicher machen zu müssen.
* 1 Ruby sammelt Objekte, die nicht mehr verwendet werden, und gibt den Speicher automatisch frei.
Daher gibt es Fälle, in denen der Produktionsserver plötzlich ausfällt (aufgrund eines Speicherfehlers), ohne es zu bemerken, obwohl die Implementierung so ist, dass der Speicher unwissentlich aufgebraucht wird.
Der Grund, warum ich das sagen kann, ist, dass dieses Phänomen an dem Ort aufgetreten ist, an dem ich gerade arbeite.
Ich war für die Implementierung verantwortlich, habe aber viel aus dieser Erfahrung gelernt, daher werde ich eine Notiz hinterlassen, damit ich sie nicht vergesse.
Zunächst müssen Sie untersuchen, wo der Speicherfehler auftritt.
Ich habe "ObjectSpace.memsize_of_all" verwendet, um die Speichernutzung in Rails zu untersuchen.
Mit dieser Methode können Sie die Speichernutzung untersuchen, die von allen lebenden Objekten in Bytes verbraucht wird.
Legen Sie diese Methode als Prüfpunkt an der Stelle fest, an der der Ausführungsprozess wahrscheinlich abfällt, und untersuchen Sie ständig, wo viel Speicherplatz verbraucht wird.
■ Verwendungsbeispiel zum Überprüfen der Speichernutzung
class Hoge
def self.hoge
puts 'Anzahl der Objektspeicher vor Speichererweiterung durch Karte'
puts '↓'
puts ObjectSpace.memsize_of_all <====Kontrollpunkt
array = ('a'..'z').to_a
array.map do |item| <==== ①
puts "#{item}Anzahl der Objektspeicher von"
puts '↓'
puts ObjectSpace.memsize_of_all <====Kontrollpunkt
item.upcase
end
end
end
■ Ausführungsergebnis
irb(main):001:0> Hoge.hoge
Anzahl der Objektspeicher vor Speichererweiterung durch Karte
↓
137789340561
Anzahl der Objektspeicher von a
↓
137789342473
Anzahl der Objektspeicher in b
↓
137789342761
Anzahl der Objektspeicher in c
↓
137789343049
Anzahl der Objektspeicher in d
↓
137789343337
Anzahl der Objektspeicher von e
↓
137789343625
.
.
.
Anzahl der Objektspeicher von x
↓
137789349097
Anzahl der Objektspeicher von y
↓
137789349385
Anzahl der Objektspeicher in z
↓
137789349673
=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
Anhand dieses Ausführungsergebnisses können Sie erkennen, dass die von der Karte übergebenen Daten zuerst sofort im Speicher erweitert werden und der Speicherverbrauch dort gestiegen ist. (Teil ①)
Sie können auch sehen, dass der Speicherverbrauch mit jedem Schleifenprozess zunimmt.
Wenn der Vorgang wie dieser Beispielcode einfach ist, gibt es kein Problem.
Wenn die übertragene Datenmenge groß ist und die durch die Schleifenverarbeitung durchgeführte Implementierung kompliziert ist, wird der Speicherverbrauch verringert.
** Ich erhalte einen Speicherfehler (ein Fehler, der auftritt, wenn die Speicherverarbeitung nicht mithalten kann). ** **.
Diese Untersuchung wurde auch durch das obige Verfahren untersucht, und als Ergebnis wurde der Schluss gezogen, dass ein Speicherfehler auftrat, weil die übertragene Datenmenge groß war und die umfangreiche Verarbeitung des Ausspuckens von Abfragen mit Karte implementiert wurde.
Ich verstehe die Ursache.
Lassen Sie uns als nächstes über Gegenmaßnahmen nachdenken.
Die ersten Maßnahmen, die ich mir ausgedacht habe, sind die folgenden drei.
1.Erhöhen Sie das Gedächtnis mit der Kraft des Geldes
2. Thread(Faden)Parallelverarbeitung mit
3.Stapelverarbeitung
Um ehrlich zu sein, ist dies die schnellste und Sie müssen nur die Speicherspezifikationen des Servers mit der Kraft des Geldes erhöhen, also lasst uns das tun!
Ich dachte.
Es gibt keine andere speicherintensive Implementierung als diesen Prozess, daher hielt ich es für dumm, nur für diesen Teil Geld auszugeben, also habe ich diese Idee gestoppt.
Ich habe mir die Parallelverarbeitung von Ruby als nächste Gegenmaßnahme ausgedacht, aber wenn der Engpass die Verarbeitungszeit (Timeout) ist, ist dies korrekt, da es schneller ist, wenn mehrere Threads parallel eingerichtet und berechnet und zusammengeführt werden, diesmal jedoch Da der Engpass der Speicherdruck aufgrund eines Speicherfehlers ist, ändert sich die Datenmenge, die von mehreren Threads verarbeitet wird, nicht. Daher wird erwartet, dass am Ende ein Speicherfehler auftritt. Daher habe ich diese Idee gestoppt.
Die Hauptursache für diesen Speicherfehler ist ein Speicherfehler, der auftritt, wenn eine große Datenmenge gleichzeitig erweitert wird und die Verarbeitung mit hoher Last in einer Schleife wiederholt wird.
Daher dachte ich, dass es gut wäre, wenn eine große Datenmenge implementiert werden könnte, während Speicher gespart wird, indem sie durch Stapelverarbeitung in Einheiten wie 1.000 aufgeteilt wird, ohne den Speicher sofort zu erweitern.
Rails bietet eine Methode namens "find_in_batches", mit der standardmäßig 1000 Elemente gleichzeitig verarbeitet werden können.
Beispiel) 10,1 für 000,Teilen Sie in 000 Prozesse und in 10 Batch-Prozesse.
find_in_Ein Bild, das weniger Speicher benötigt, indem die Verarbeitung mit Stapeln eingeschränkt wird.
** Stapelverarbeitung mit find_in_batches **
Sobald Sie wissen, wie Sie damit umgehen sollen, müssen Sie es nur noch implementieren.
Lassen Sie es uns implementieren. (Da es nicht möglich ist, den Buchungskreis tatsächlich anzuzeigen, wird nur das Bild angezeigt.)
■ Implementierungsimage
User.find_in_batches(batch_size: 1000) do |users|
#Etwas zu verarbeiten
end
Selbst wenn 10.000 Benutzerdaten erfasst werden, werden 1000 mithilfe von find_in_batches verarbeitet.
Mit anderen Worten, es ist ein Bild, das in 10.000 / 1000 = 10 Prozesse unterteilt ist.
Der Speicherverbrauch wurde auf 1/100 reduziert.
** Der größte Nachteil dieser Implementierung ist jedoch, dass sie zu viel Verarbeitungszeit benötigt. ** **.
Wenn Sie Heroku usw. verwenden, führt diese Implementierung zu einem ** RequestTimeOut-Fehler * 1 **.
* 1 In Heroku führt eine Verarbeitung, die 30 Sekunden oder länger dauert, zu einem Request Time Out-Fehler.
Daher denke ich, dass es besser ist, diese Implementierung mit hoher Lastverarbeitung auf die Hintergrundverarbeitung zu verschieben.
Wenn Sie Rails verwenden, können Sie dies mit Sidekiq tun.
Ich denke, Sie sollten mit dem folgenden Verfahren arbeiten.
STEP1. find_in_Verwenden Sie Stapel, um den Speicherverbrauch zu reduzieren
STEP2.Wenn STEP1 abgeschlossen ist, dauert es einige Zeit, sollte jedoch ohne Speicherfehler funktionieren.
Die Verarbeitung dauert jedoch einige Zeit. Verschieben Sie diesen Prozess daher in den Hintergrund.
Zuerst dachte ich, es sei eine nervige Aufgabe.
Ich habe viel gelernt und bin froh, dass ich es jetzt implementiert habe.
https://techblog.lclco.com/entry/2019/07/31/180000 https://qiita.com/kinushu/items/a2ec4078410284b9856d
Recommended Posts