Je connaissais le "problème N + 1" jusqu'à présent, mais je n'avais jamais fait un développement à une telle échelle, j'ai donc passé mon temps sans m'en rendre compte. .. Cependant, lorsque j'ai effectivement travaillé sur le terrain, j'ai réalisé que dans le développement à grande échelle, il y avait des dizaines de milliers de données dans Zara, donc je dois en être conscient à partir de maintenant (pleurer). Je pense que tout le monde a besoin de connaissances qui sont presque toujours nécessaires quand ils entrent réellement sur le site, alors veuillez les maintenir ici! !!
En termes simples, le fait que SQL soit émis à chaque fois pendant le traitement de la boucle pose un problème et qu'une grande quantité (plus que nécessaire) de SQL est émise, ce qui entraîne de mauvaises performances. Laissez-moi vous donner une analogie. Lorsque vous souhaitez obtenir 10 produits de l'utilisateur nommé M. A et afficher la page de liste des produits de M. A,
Une fois pour obtenir les données utilisateur de M. A 10 fois pour obtenir 10 données produits Vous émettez un total de 11 requêtes pour obtenir les données que vous souhaitez afficher.
Ce n'est pas un gros problème avec environ 11 cas, mais c'est difficile s'il s'agit de 10 000 ou 20 000 cas. En supposant qu'une requête prend 0,001 seconde, 10000 ou 20000 requêtes prendront 10 ou 20 secondes pour répondre. C'est un problème d'attendre si loin, non? Ce problème peut être résolu en fonction de la manière dont le code est écrit, Si vous l'écrivez correctement, même si vous avez 10000 produits, vous pouvez l'obtenir avec deux requêtes (rires)
Voyons maintenant comment résoudre ce problème. Fondamentalement, vous pouvez utiliser les quatre méthodes présentées ci-dessous. L'utilisation est légèrement différente, rendons donc possible de l'utiliser correctement.
Méthode | cache | Requete | Utilisation | Référence des données |
---|---|---|---|---|
joins | ne pas faire | INNER JOIN | Rétrécir | ça peut |
eager_load | Faire | LEFT JOIN | Trésorerie et raffinement | ça peut |
preload | Faire | SELECT chacun sans JOIN | cache | Ne peux pas |
includes | Faire | Selon le cas | Espèces, affiner si nécessaire | ça peut |
joins INNER JOIN est exécuté par défaut. Utilisez left_joins lorsque vous souhaitez effectuer une jointure externe gauche. Cette méthode ne met pas en cache, donc la mémoire peut être réduite au minimum. En outre, vous pouvez l'utiliser lorsque vous n'avez besoin que du résultat de réduction sans faire référence aux données de la destination JOIN.
User.joins(:products).where(products: { id: 1 })
# SELECT `users`.* FROM `users` INNER JOIN `products` ON `products`.`user_id` = `users`.`id` WHERE `products`.`id` = 1
eager_load Dessinez l'association spécifiée avec LEFT OUTER JOIN et mettez-la en cache. Le traitement à grande vitesse est possible car une seule requête est requise. Vous pouvez l'utiliser lorsque vous souhaitez REJOINDRE une association 1-to-1 ou N-to-1 (appartient_à, has_one), ou lorsque vous souhaitez vous référer aux informations de la table à laquelle vous vous êtes joint (par exemple, réduire par Où).
User.eager_load(:products)
# SELECT `users`.`id`, `users`.`name`, `users`.`created_at`, `users`.`updated_at`, `products`.`id`, `products`.`user_id`, `products`.`created_at`, `products`.`updated_at` FROM `users` LEFT OUTER JOIN `products` ON `products`.`user_id` = `users`.`id`
preload L'association spécifiée est divisée en plusieurs requêtes et soustraite pour la mise en cache. Cependant, il n'est pas possible de se référer aux données de la destination de l'association (comme la réduction par Où). Utilisez-le pour les associations plusieurs-à-plusieurs. En guise de mise en garde, si la quantité de données est importante, la clause IN a tendance à être importante, ce qui peut exercer une pression sur la mémoire.
User.preload(:products)
# SELECT `users`.* FROM `users`
# SELECT `products`.* FROM `products` WHERE `products`.`user_id` IN (1, 2, 3, ...)
includes En termes simples, il utilise correctement eager_load et preload. Cependant, il semble préférable de ne pas utiliser d'inclus. Parce que comprend les sortes de préchargement et eager_load bien. Si vous comprenez les caractéristiques de preload et eager_load, vous verrez rarement des inclusions. Cela peut ne pas poser de problème si vous les incluez alors que les données sont petites, mais à mesure que les données augmentent, des plis et des problèmes deviendront apparents, assurez-vous donc de connaître correctement le comportement des inclusions.
User.includes(:products)
# SELECT `users`.* FROM `users`
# SELECT `products`.* FROM `products` WHERE `products`.`user_id` IN (1, 2, 3, ...)
Peu importe à quel point vous êtes prudent, les êtres humains sortiront. Bullet est le joyau qui couvre cela.
group :optimization do
gem 'bullet', '~> 6.1.0'
end
config/enviroments/optomization.rb
config.after_initialize do
#Permet l'optimisation.
Bullet.enable = true
#Il est permis d'afficher le problème avec JS Alert.
Bullet.alert = true
#Permet la journalisation dans un fichier.
Bullet.bullet_logger = true
#N sur la console du navigateur+1 Vous permet de visualiser le problème.
Bullet.console = true
#Permet à Bullet de se connecter à Rails.
Bullet.rails_logger = true
#Permet d'afficher le problème dans le pied de page.
Bullet.add_footer = true
end
webpacker.yml
optimization:
<<: *development
$ bundle exec rails server -e optimization
config/enviroments/test.rb
config.after_initialize do
Bullet.enable = true
Bullet.bullet_logger = true
Bullet.raise = false
end
spec/spec_helper.rb
if Bullet.enable?
config.before(:each) do
Bullet.start_request
end
config.after(:each) do
Bullet.perform_out_of_channel_notifications if Bullet.notification?
Bullet.end_request
end
end
Jusqu'à présent, nous avons vu comment traiter le problème N + 1. Je pense que la réponse sera beaucoup plus rapide rien qu'en changeant un peu de conscience! Maintenant, développons consciemment à partir de ce moment! !! !!
[Découvrez les problèmes de requête N + 1 cachés dans le code Ruby on Rails avec le gem Bullet et optimisez la réponse du site Rails](https://www.123ish.com/jp/entries/2236-ruby-on- rails% E3% 81% AE% E3% 82% B3% E3% 83% BC% E3% 83% 89% E3% 81% AB% E6% BD% 9C% E3% 82% 80n-1% E3% 82% AF% E3% 82% A8% E3% 83% AA% E5% 95% 8F% E9% A1% 8C% E3% 82% 92bullet-gem-% E3% 81% A7% E7% 99% BA% E8% A6 % 8B% E3% 81% 97% E3% 81% A6% E3% 80% 81rail% E3% 82% B5% E3% 82% A4% E3% 83% 88% E3% 81% AE% E3% 83% AC % E3% 82% B9% E3% 83% 9D% E3% 83% B3% E3% 82% B9% E3% 82% 92% E6% 9C% 80% E9% 81% A9% E5% 8C% 96)
Exécutez Bullet dans l'environnement de test
Les raisons d'utiliser correctement précharge et eager_load sans utiliser ActiveRecord incluent
L'utilisation personnelle d'ActiveRecord inclut, préchargement, eager_load
Recommended Posts