Ich kann Ihnen nicht sagen, die Quelle des Speicherlecks, aber ich Spion etwas niedrig hängenden Früchte.
Aber zuerst zwei Dinge:
Sind Sie sicher, dass Active der richtige Weg ist, Daten von einer Datenbank in einer anderen zu kopieren? Ich bin sehr zuversichtlich, dass es nicht ist. Jedes wichtige Datenbankprodukt verfügt über robuste Export- und Importfunktionen, und die Leistung, die Sie dort sehen, wird um ein Vielfaches besser sein als in Ruby, und Sie können diese Tools immer in Ihrer App aufrufen. Denken Sie darüber nach, bevor Sie diesen Weg fortsetzen.
Woher kommt die Nummer 10.000? Ihr Code schlägt vor, dass Sie wissen, dass es keine gute Idee ist, alle der Datensätze auf einmal zu holen, aber 10.000 ist immer noch eine Menge von Datensätzen. Sie können einige Gewinne sehen, indem Sie einfach verschiedene Zahlen ausprobieren: 100 oder 1000, sagen wir.
das gesagt ist, wollen wir in graben, was diese Linie zu tun ist:
users = User.limit(10000).offset(offset).as_json
Der erste Teil, User.limit(10000).offset(offset)
schafft eine Activerecord :: Relation Objekt darstellt Ihre Anfrage. Wenn Sie as_json
darauf aufrufen, wird die Abfrage ausgeführt, die 10.000 Benutzermodellobjekte instanziiert und in ein Array einfügt, und anschließend wird aus jedem dieser Benutzerobjektattribute ein Hash erstellt. (Werfen Sie einen Blick auf die Quelle für ActiveRecord::Relation#as_json
here.)
Mit anderen Worten, Sie erstellen 10.000 Benutzerobjekte nur, um sie wegwerfen, nachdem Sie ihre Attribute haben.
Also, ein schneller Gewinn ist es, diesen Teil ganz zu überspringen. Markieren Sie die Rohdaten:
user_keys = User.attribute_names
until break_condition
# ...
users_values = User.limit(10000).offset(offset).pluck(user_keys)
users_values.each do |vals|
user_attrs = user_keys.zip(vals).to_h
member = Member.find_by(name: user_attrs["name"])
member.update_attributes(user_attrs)
end
end
ActiveRecord::Calculations#pluck
gibt einen Array von Arrays mit den Werten aus jedem Datensatz.Innerhalb der user_values.each
Schleife drehen wir dieses Werte-Array in einen Hash. Es müssen keine Benutzerobjekte instanziiert werden.
Lassen Sie uns jetzt einen Blick auf diese:
member = Member.find_by(name: user_attrs["name"])
member.update_attributes(user_attrs)
Dieser einen Datensatz aus der Datenbank auswählt, instanziiert ein Mitglied Objekt und aktualisiert anschließend den Datensatz in der Datenbank-10.000-mal in jeder Iteration der while
Schleife . Dies ist der korrekte Ansatz Wenn Sie müssen überprüft werden, wenn der Datensatz aktualisiert wird. Wenn Sie nicht Validierungen ausführen müssen, obwohl, können Sie Zeit und Speicher durch, wieder speichern, keine Objekte instanziieren:
Member.where(name: user_attrs["name"]).update_all(user_attrs)
Der Unterschied besteht darin, dass ActiveRecord::Relation#update_all
nicht den Datensatz aus der Datenbank auswählt oder instanziiert ein Member-Objekt, es aktualisiert es nur. Sie haben in Ihrem Kommentar oben gesagt, dass Sie eine eindeutige Einschränkung für die Spalte name
haben, daher wissen wir, dass dadurch nur ein einzelner Datensatz aktualisiert wird.
Nachdem Sie diese Änderungen vorgenommen haben, müssen Sie immer noch mit der Tatsache kämpfen, dass Sie in jeder Iteration der Schleife while
10.000 UPDATE-Abfragen durchführen müssen. Erneut sollten Sie in Erwägung ziehen, die integrierte Export- und Importfunktionalität Ihrer Datenbanken zu verwenden, anstatt zu versuchen, Rails dies zu ermöglichen.
'wann'? Meinst du "während"? – matt
'mehr oder weniger so aussehen 'ist es vielleicht besser, genauen Code zu zeigen? – fl00r
@ fl00r Es ist genau Code erwarten, dass der Klassen- oder Modellname geändert wird – Viren