[RAILS] Unterschätzen Sie das N + 1-Problem nicht!

Einführung

Ich kannte das "N + 1-Problem" bis jetzt, aber ich hatte noch nie eine so groß angelegte Entwicklung durchgeführt, also habe ich meine Zeit verbracht, ohne mir dessen bewusst zu sein. .. Als ich jedoch tatsächlich vor Ort arbeitete, stellte ich fest, dass es in der groß angelegten Entwicklung Zehntausende von Daten in Zara gibt, daher muss ich mich von nun an dessen bewusst sein (weinen). Ich denke, dass jeder Wissen braucht, das fast immer notwendig ist, wenn er die Site tatsächlich betritt. Bitte halten Sie es hier gedrückt! !!

Was ist das N + 1-Problem?

Einfach ausgedrückt ist es ein Problem, dass SQL jedes Mal während der Schleifenverarbeitung ausgegeben wird und eine große Menge (mehr als erforderlich) von SQL ausgegeben wird, was zu einer schlechten Leistung führt. Lassen Sie mich Ihnen eine Analogie geben. Wenn Sie 10 Produkte des Benutzers mit dem Namen Mr. A erhalten und die Produktlistenseite von Mr. A anzeigen möchten,

Einmal, um die Benutzerdaten von Herrn A zu erhalten 10 Mal, um 10 Produktdaten zu erhalten Sie geben insgesamt 11 Abfragen aus, um die Daten abzurufen, die Sie anzeigen möchten.

Es ist keine große Sache mit ungefähr 11 Fällen, aber es ist schwierig, wenn dies 10.000 oder 20.000 sind. Angenommen, eine Abfrage dauert 0,001 Sekunden, dauert die Beantwortung von 10000 oder 20000 Abfragen 10 oder 20 Sekunden. Es ist mühsam, so weit zu warten, oder? Dieses Problem kann je nach Schreibweise des Codes gelöst werden. Wenn Sie es richtig schreiben, können Sie es mit zwei Abfragen erhalten, selbst wenn Sie 10.000 Produkte haben (lacht)

Methoden zur Lösung von Problemen

Nun wollen wir sehen, wie dieses Problem gelöst werden kann. Grundsätzlich können Sie die vier unten vorgestellten Methoden verwenden. Die Verwendung ist etwas anders, also machen wir es möglich, sie richtig zu verwenden.

Methode Zwischenspeicher Abfrage Verwenden Datenreferenz
joins TU es nicht INNER JOIN Eingrenzen es kann
eager_load Machen LEFT JOIN Bargeld und Raffinesse es kann
preload Machen WÄHLEN Sie jeweils ohne JOIN Zwischenspeicher Kann nicht
includes Machen Je nach Fall Bargeld, bei Bedarf eingrenzen es kann

joins INNER JOIN wird standardmäßig ausgeführt. Verwenden Sie left_joins, wenn Sie LEFT OUTER JOIN ausführen möchten. Diese Methode wird nicht zwischengespeichert, sodass der Speicher auf ein Minimum beschränkt werden kann. Sie können es auch verwenden, wenn Sie nur das Eingrenzungsergebnis benötigen, ohne auf die Daten des JOIN-Ziels zu verweisen.


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 Zeichnen Sie die angegebene Zuordnung mit LEFT OUTER JOIN und zwischenspeichern Sie sie. Eine schnelle Verarbeitung ist möglich, da nur eine Abfrage erforderlich ist. Sie können es verwenden, wenn Sie einer 1-zu-1- oder N-zu-1-Zuordnung beitreten möchten (Gehört zu, hat_one) oder wenn Sie auf die Informationen der Tabelle verweisen möchten, zu der Sie beigetreten sind (z. B. Eingrenzen nach Wo).


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 Die angegebene Zuordnung wird in mehrere Abfragen unterteilt und zum Zwischenspeichern abgezogen. Es ist jedoch nicht möglich, auf die Daten des Zuordnungsziels zu verweisen (z. B. Eingrenzen nach Wo). Verwenden Sie es für viele-zu-viele Assoziationen. Als Einschränkung: Wenn die Datenmenge groß ist, ist die IN-Klausel tendenziell groß, was Druck auf den Speicher ausüben kann.


User.preload(:products)
# SELECT `users`.* FROM `users`
# SELECT `products`.* FROM `products` WHERE `products`.`user_id` IN (1, 2, 3, ...)

includes Einfach ausgedrückt, es verwendet eifrig_load und Preload richtig. Es scheint jedoch besser, keine Includes zu verwenden. Weil enthält Sortierungen Preload und eifrige_load schön. Wenn Sie die Eigenschaften von Preload und Zeal_load verstehen, werden Sie selten Includes sehen. Es ist möglicherweise kein Problem, wenn Sie es einschließen, solange nur wenige Daten vorhanden sind. Mit zunehmender Datenmenge treten jedoch Falten und Probleme auf. Stellen Sie daher sicher, dass Sie das Verhalten von Includes korrekt kennen.


User.includes(:products)
# SELECT `users`.* FROM `users`
# SELECT `products`.* FROM `products` WHERE `products`.`user_id` IN (1, 2, 3, ...)

Master Bullet Edelstein und frühzeitig N + 1 Probleme erkennen

Egal wie vorsichtig Sie sind, die Menschen werden herauskommen. Bullet ist ein Juwel, das das abdeckt.

Wie benutzt man Bullet?

  1. Fügen Sie der Gemfile Folgendes hinzu.

group :optimization do
  gem 'bullet', '~> 6.1.0'
end
  1. Kopieren Sie mit der Ausführungsumgebung als Optimierung die Datei development.rb und beschreiben Sie Folgendes.

config/enviroments/optomization.rb




config.after_initialize do
  #Ermöglicht die Optimierung.
  Bullet.enable = true

  #Es ist zulässig, das Problem mit JS Alert anzuzeigen.
  Bullet.alert = true

  #Ermöglicht die Protokollierung in einer Datei.
  Bullet.bullet_logger = true

  #N in der Browserkonsole+1 Ermöglicht das Anzeigen des Problems.
  Bullet.console = true

  #Ermöglicht Bullet, sich bei Rails anzumelden.
  Bullet.rails_logger = true

  #Ermöglicht die Anzeige des Problems in der Fußzeile.
  Bullet.add_footer = true
end
  1. Fügen Sie webpacker.yml Folgendes hinzu.

webpacker.yml



optimization:
  <<: *development
  1. Führen Sie N + 1 aus und suchen Sie nach N + 1-Problemen.

$ bundle exec rails server -e optimization

So stellen Sie Bullet in einer Testumgebung ein

  1. Fügen Sie test.rb Folgendes hinzu.

config/enviroments/test.rb



config.after_initialize do
  Bullet.enable = true
  Bullet.bullet_logger = true
  Bullet.raise = false
end
  1. Fügen Sie spec_helper.rb Folgendes hinzu.

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

Am Ende

Bisher haben wir gesehen, wie wir mit dem N + 1-Problem umgehen können. Ich denke, dass die Reaktion viel schneller sein wird, wenn man nur ein wenig das Bewusstsein ändert! Lassen Sie uns nun bewusst aus dieser Zeit entwickeln! !! !!

Referenz

[Entdecken Sie N + 1-Abfrageprobleme, die im Ruby on Rails-Code mit Bullet gem verborgen sind, und optimieren Sie die Reaktion der Rails-Site](https://www.123ish.com/jp/entries/2236-ruby-on- Schienen% 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% 92 Bullet-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)

Bullet in Testumgebung ausführen

Grund für die ordnungsgemäße Verwendung von Preload und Zeal_load ohne Verwendung von ActiveRecord:

Der persönliche Gebrauch von ActiveRecord umfasst Preload, eifriges Laden

Unterschiede zwischen ActiveRecord-Joins, Preloads, Includes und eifrigem Laden

Recommended Posts

Unterschätzen Sie das N + 1-Problem nicht!
[N + 1 Problem]
[Anfänger] Entdecken Sie das N + 1-Problem! Wie benutzt man Bullet?
Lösen wir das FizzBuzz-Problem!
Ich habe das FizzBuzz-Problem ausprobiert
Ein Memorandum über das FizzBuzz-Problem
Strict_loading-Funktion zur Unterdrückung des Auftretens von N + 1-Problemen, die von Schienen hinzugefügt wurden 6.1