Voici quelques conseils pour vous aider à améliorer vos performances dans Rails. Nous avons une large gamme d'articles, de ceux qui sont prêts à être utilisés à ceux qui nécessitent un certain temps pour s'améliorer.
Nous espérons que cela aidera à améliorer les performances de l'application en cours de développement.
Si N + 1 se produit de toute façon, essayez de l'éliminer. Dans la plupart des cas, cela devrait être le goulot d'étranglement des performances de votre application.
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 %> #Ici N+1 se passe
<% end %>
Dans l'exemple de code ci-dessus, N + 1 se produit lors de la lecture de l'utilisateur associé au livre.
Réécrivons le contrôleur comme suit.
books_controller.rb
class BooksController < ApplicationController
def index
@book = Book.includes(:user)
end
end
Lorsque tous les modèles ont été acquis dans "model.all", il y a une forte possibilité que N + 1 se produise, et tout n'est souvent pas utilisé.
Il est également recommandé d'installer la gemme bullet
dans l'application pour détecter N + 1.
https://github.com/flyerhzm/bullet
De plus, dans l'exemple ci-dessus, ʻincludes est utilisé, mais ce serait bien de pouvoir utiliser correctement
preload et ʻeager_load
.
Différence entre les jointures d'enregistrement actif, préchargement, inclut et eager_load
Si vous utilisez count, vous émettez SQL.
Par conséquent, si vous souhaitez vérifier le nombre de modèles, utilisez plutôt size
.
C'est un cas qui a tendance à se produire de manière inattendue, mais c'est facile parce que vous remplacez simplement count
par size
.
** Lors de l'utilisation de count **
user = User.all
user.count
#Une instruction SELECT utilisant la fonction de comptage est émise
(4.6ms) SELECT COUNT(*) FROM `users`
=> 100
** Lors de l'utilisation de la taille **
user = User.all
user.size
#La requête SQL n'est pas émise
=> 100
Comme count
, ʻexist?Émettra SQL s'il est utilisé sur un objet modèle. Si vous voulez vérifier s'il existe, utilisez
present?` À la place.
*** Lors de l'utilisation de la sortie? ***
user = User.where(deleted: true)
user.exist?
#SQL est émis
User Load (5.4ms) SELECT `users`.* FROM `users` WHERE `users`.`deleted` = TRUE
=> []
*** Lors de l'utilisation de présent? ***
user = User.where(deleted: true)
user.present?
#SQL n'est pas émis
=> []
C'est un modèle comme obtenir tous les utilisateurs avec «tous» et le tourner avec chacun. C'est un modèle courant dans le traitement par lots.
User.all.each do |user|
#Code qui fait quelque chose en utilisant l'objet de l'utilisateur
end
Comme je l'ai mentionné dans les conseils N + 1, une fois que tout est entré dans le processus, suspectez le code.
Avec l'implémentation ci-dessus, après avoir étendu tous les cas utilisateur en mémoire, chaque processus est exécuté par chacun, de sorte que la consommation de mémoire devient lourde.
Utilisez find_each au lieu de tout.
User.find_each do |user|
#Code qui fait quelque chose en utilisant l'objet de l'utilisateur
end
find_each récupère 1000 enregistrements à la fois et traite les enregistrements récupérés un par un.
Après avoir acquis 1000 objets, les 1000 objets suivants seront à nouveau acquis, et ainsi de suite.
En passant, si vous souhaitez spécifier le nombre d'expansions d'enregistrement, utilisez sa méthode sœur, find_in_batches. Vous pouvez spécifier le nombre d'enregistrements dans l'argument de la méthode.
Bien que pas autant que le problème N + 1 et chaque modèle mentionné ci-dessus, c'est aussi une implémentation qui a tendance à être faite si vous ne faites pas attention.
user_names = User.all.map(&:name)
L'approche cartographique décrite ci-dessus crée des objets Active Record inutiles et n'est pas très performante.
Étant donné que les objets Active Record englobent un grand nombre de modules et de méthodes, ils sont coûteux à générer et à consommer seuls de la mémoire.
S'il existe un moyen de ne pas créer d'objet Active Record, pensez-y.
user_names = User.pluck(:name)
En utilisant pluck, nous avons pu éviter la création d'objets inutiles.
Vous pouvez également utiliser un cache ou rendre le processus asynchrone lorsque les performances se détériorent.
** Utiliser le cache ** Pensez à introduire Redis, etc. Il peut être judicieux d'envisager de lire les données de base. Cependant, si vous ne considérez pas attentivement l'emplacement d'introduction, cela a tendance à provoquer des bogues.
** Rendre le traitement asynchrone ** Le traitement lourd devient asynchrone avec «sidekiq» et «travail retardé» de la gemme. Je vois souvent comment rendre le processus d'envoi de courrier asynchrone.
Comment était-ce. Jusqu'à présent, nous avons introduit des astuces qui peuvent facilement améliorer les performances simplement en étant conscient du codage avec Rails.
S'il y a d'autres façons de faire cela, ou s'il y a quelque chose qui ne va pas ici, veuillez me le faire savoir dans la section commentaires
Recommended Posts