[RUBY] [Rails] Une collection de conseils qui sont immédiatement utiles pour améliorer les performances

introduction

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.

[Astuces 1] Améliorer N + 1

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

[Astuces 2] Utilisez la taille au lieu du nombre

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

[Astuces 3] Ne pas utiliser existe?

Comme count, ʻexist?Émettra SQL s'il est utilisé sur un objet modèle. Si vous voulez vérifier s'il existe, utilisezpresent?` À 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
=> []

[Astuces 4] Ne tournez pas le résultat obtenu par tous avec chaque

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.

[Astuces 5] Génération d'objets Active Record inutiles

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.

[Astuces 6] Pensez à introduire le cache et le traitement asynchrone

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.

À la fin

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

[Rails] Une collection de conseils qui sont immédiatement utiles pour améliorer les performances
Une collection de raccourcis Eclipse que les nouveaux diplômés trouvent utiles
C'est juste maintenant, mais une collection de commandes qui apparaissent fréquemment dans Rails
Une collection de questions simples pour les débutants Java
Collection RSpec que j'ai fréquemment utilisée
[Rails] Volume qui affiche les favoris et une liste de favoris
Connaissance de base de l'informatique et de sa pratique que vous devez connaître pour améliorer les performances
Générer une collection unique de valeurs à partir d'une collection qui contient des valeurs en double
Explication de Ruby on rails pour les débutants ③ ~ Création d'une base de données ~
Une percée dans laquelle vous n'êtes pas doué pour l'auto-apprentissage et comptez immédiatement sur les autres (pour vous-même)
Collection Gradle TIPS (pour moi)