Hier sind einige Tipps, mit denen Sie Ihre Leistung in Rails verbessern können. Wir haben eine große Auswahl an Artikeln, von gebrauchsfertigen bis hin zu Verbesserungen, deren Verbesserung einige Zeit in Anspruch nimmt.
Wir hoffen, dass dies dazu beiträgt, die Leistung der in der Entwicklung befindlichen App zu verbessern.
Wenn N + 1 trotzdem auftritt, versuchen Sie es zu beseitigen. In den meisten Fällen sollte dies der Leistungsengpass für Ihre App sein.
books_controller.rb
class BooksController < ApplicationController
def index
@book = Book.all
end
end
erb:index.html.erb
<% @book.each do |book|
<%= book.title %>
<%= book.user.name %> #Hier N.+1 passiert
<% end %>
Im obigen Codebeispiel tritt N + 1 auf, wenn der dem Buch zugeordnete Benutzer gelesen wird.
Schreiben wir den Controller wie folgt um.
books_controller.rb
class BooksController < ApplicationController
def index
@book = Book.includes(:user)
end
end
Wenn alle Modelle in model.all
erfasst wurden, besteht eine hohe Wahrscheinlichkeit, dass N + 1 auftritt, und häufig wird nicht alles verwendet.
Es wird auch empfohlen, die Edelsteinkugel in der App zu installieren, um N + 1 zu erkennen.
https://github.com/flyerhzm/bullet
Im obigen Beispiel wird auch "Includes" verwendet, aber es wäre schön, "Preload" und "eifriges Laden" richtig verwenden zu können.
Unterschiede zwischen ActiveRecord-Joins, Preloads, Includes und eifrigem Laden
Wenn Sie count verwenden, geben Sie SQL aus. Wenn Sie daher die Anzahl der Modelle überprüfen möchten, verwenden Sie stattdessen "Größe".
Es ist ein Fall, der normalerweise unerwartet auftritt, aber es ist einfach, weil Sie einfach "count" durch "size" ersetzen.
** Bei Verwendung von count **
user = User.all
user.count
#Eine SELECT-Anweisung mit der Zählfunktion wird ausgegeben
(4.6ms) SELECT COUNT(*) FROM `users`
=> 100
** Bei Verwendung von Größe **
user = User.all
user.size
#SQL-Abfrage wird nicht ausgegeben
=> 100
Wie count
, exist?
Gibt SQL aus, wenn es für ein Modellobjekt verwendet wird.
Wenn Sie überprüfen möchten, ob es vorhanden ist, verwenden Sie stattdessen "vorhanden".
*** Bei Verwendung von exit? ***
user = User.where(deleted: true)
user.exist?
#SQL wird ausgegeben
User Load (5.4ms) SELECT `users`.* FROM `users` WHERE `users`.`deleted` = TRUE
=> []
*** Bei Verwendung von Geschenk? ***
user = User.where(deleted: true)
user.present?
#SQL wird nicht ausgegeben
=> []
Es ist ein Muster, wie alle Benutzer mit "alle" zu bekommen und es mit jedem zu drehen. Es ist ein Muster, das in der Stapelverarbeitung üblich ist.
User.all.each do |user|
#Code, der etwas mit dem Objekt des Benutzers tut
end
Wie ich in den N + 1-Tipps erwähnt habe, verdächtigen Sie den Code, sobald alles in den Prozess eingeht.
Bei der obigen Implementierung wird nach dem Erweitern aller Benutzerfälle auf den Speicher jeder Prozess von jedem ausgeführt, sodass der Speicherverbrauch hoch wird.
Verwenden Sie find_each anstelle von all.
User.find_each do |user|
#Code, der etwas mit dem Objekt des Benutzers tut
end
find_each ruft jeweils 1000 Datensätze ab und verarbeitet die abgerufenen Datensätze nacheinander.
Nach dem Erwerb von 1000 Gegenständen werden die nächsten 1000 Gegenstände erneut erworben und so weiter.
Wenn Sie die Anzahl der Datensatzerweiterungen angeben möchten, verwenden Sie übrigens die Schwestermethode find_in_batches. Sie können die Anzahl der Datensätze im Argument der Methode angeben.
Obwohl nicht so viel wie das N + 1-Problem und jedes oben erwähnte Muster, ist dies auch eine Implementierung, die in der Regel durchgeführt wird, wenn Sie nicht vorsichtig sind.
user_names = User.all.map(&:name)
Der oben beschriebene kartenbasierte Ansatz erzeugt unnötige Active Record-Objekte und ist nicht sehr performant.
Da Active Record-Objekte eine große Anzahl von Modulen und Methoden umfassen, ist es teuer, sie selbst zu generieren und Speicher zu verbrauchen.
Wenn es eine Möglichkeit gibt, kein Active Record-Objekt zu erstellen, denken Sie darüber nach.
user_names = User.pluck(:name)
Durch Zupfen konnten wir unnötige Objekterstellung vermeiden.
Sie können auch einen Cache verwenden oder den Prozess asynchron machen, wenn sich die Leistung verschlechtert.
** Cache verwenden ** Erwägen Sie die Einführung von Redis usw. Es ist möglicherweise eine gute Idee, Stammdaten zu lesen. Wenn Sie den Einführungsort jedoch nicht sorgfältig prüfen, kann dies zu Fehlern führen.
** Asynchrone Verarbeitung machen ** Schwere Verarbeitung wird asynchron mit "Sidekiq" und "verzögertem Job" von Gem. Ich sehe oft, wie man den Mail-Sendeprozess asynchron macht.
Wie war das. Bisher haben wir Tipps eingeführt, mit denen sich die Leistung leicht verbessern lässt, wenn Sie beim Codieren mit Rails darauf achten.
Wenn es andere Möglichkeiten gibt oder wenn hier etwas nicht stimmt, lass es mich bitte im Kommentarbereich wissen lol
Recommended Posts