[RUBY] [Rails] Unterdrücken Sie nutzloses SQL, indem Sie die Cache-Steuerung von Zuordnungen verwenden

Active Record verfügt über eine leistungsstarke Funktion, die als Zuordnungsfunktion bezeichnet wird. In diesem Artikel haben wir zusammengefasst, wie die Zuordnungscache-Steuerung verwendet wird, um unnötiges SQL zu unterdrücken.

Was ist Assoziations-Cache-Kontrolle?

Es wird im Rails-Handbuch wie folgt beschrieben. [Active Record Association-3.1-Cache-Steuerung](https://railsguides.jp/association_basics.html#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3% 83% A5% E5% 88% B6% E5% BE% A1)

Das Ergebnis der zuletzt ausgeführten Abfrage bleibt im Cache erhalten und kann in nachfolgenden Operationen verwendet werden.

Verschieben Sie es tatsächlich und prüfen Sie, ob es zwischengespeichert ist. Verwenden Sie das folgende Modell.

class User < ApplicationRecord
  has_many :reviews
end

class Review < ApplicationRecord
  belongs_to :user
end

Wenn Sie "user.reviews" wie unten gezeigt ausführen, wird SQL bei der zweiten Ausführung nicht ausgegeben. Dies liegt daran, dass ActiveRecord das Ergebnis der ersten Ausführung zwischenspeichert und zurückgibt.

irb(main):004:0> user = User.first
  User Load (5.8ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User:0x000056307ae3aca0

irb(main):005:0> user.reviews
  Review Load (2.7ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1
=> [#<Review:0x000056307b1a77d0

irb(main):007:0> user.reviews
=> [#<Review:0x000056307b1a77d0

In diesem Beispiel sehen Sie, dass die Ausführung von SQL 2,7 ms dauert, um die Überprüfungen abzurufen. Da der Cache das zweite Mal verwendet wird, bedeutet dies, dass der gleiche Prozess 2,7 ms spart.

Es ist nicht sehr vorteilhaft, wenn nur ein SQL-Problem wie dieses Mal verkürzt wird. Wenn dies 100-mal und 1000-mal akkumuliert wird, beträgt der Unterschied einige Sekunden, und Sie können den Unterschied spüren.

Zum Zeitpunkt des Erwerbs

Verwenden Sie dasselbe Modell wie im vorherigen Beispiel. Wenn Sie das Benutzerobjekt bereits abgerufen haben, wie erhalten Sie die Bewertungen, die der Benutzer hat?

#Benutzerobjekt erfasst
user

#Holen Sie sich die Bewertungen, die der Benutzer hat
# 1
reviews = Review.where(user_id: user.id)

# 2
reviews = Review.where(user: user)

# 3
reviews = user.reviews

Die SQL, die beim Abrufen von Überprüfungen ausgegeben wird, ist für 1-3 gleich, es gibt jedoch einen Unterschied. Der Unterschied besteht darin, ob die überprüfungsbezogenen Daten "Benutzer" zwischengespeichert werden. Holen Sie sich Bewertungen mit den oben genannten Methoden 1 bis 3 mit irb und überprüfen Sie, ob die zugehörigen Daten "Benutzer" der Bewertungen zwischengespeichert sind.

#1


irb(main):009:0> reviews = Review.where(user_id: user.id)
  Review Load (1.2ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1
=> [#<Review:0x000055fc149bca10

irb(main):010:0> reviews.first.user
  User Load (0.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User:0x000055fc14b75d20

#2


irb(main):011:0> reviews = Review.where(user: user)
  Review Load (0.8ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1
=> [#<Review:0x000055fc14b83ab0

irb(main):012:0> reviews.first.user
  User Load (0.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User:0x000055fc14fca880

#3


irb(main):015:0> reviews = user.reviews
  Review Load (0.6ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1
=> [#<Review:0x000055fc1504c0b0

irb(main):016:0> reviews.first.user
=> #<User:0x000055fc14873f78

Nur im Fall von 3 wird SQL nicht ausgegeben, um Benutzer beim Ausführen von "reviews.first.user" zu erhalten. Es scheint, dass die ursprünglichen verwandten Daten zwischengespeichert werden, wenn sie durch Zuordnung erfasst werden.

Bevor ich den Vorgang tatsächlich überprüfte, hatte ich erwartet, dass die zugehörigen Daten zwischengespeichert werden, da die Methode zum Übergeben des Objekts an die Stelle wie in 2 auch das Objekt übergibt, aber es wurde so implementiert. Es scheint nicht so zu sein.

Wenn Sie verwandte Daten abrufen, ist es besser, die Zuordnung zu verwenden, um den Cache abzurufen. Verwenden Sie sie also aktiv.

Zum Zeitpunkt der Schöpfung

Verwenden Sie dasselbe Modell wie im vorherigen Beispiel. Wie erstellen Sie das Benutzerobjekt, wenn Sie es bereits abgerufen haben und diesem Benutzer eine Bewertung hinzufügen möchten?

#Benutzerobjekt erfasst
user

#Erstellen Sie eine neue Bewertung für den Benutzer
# 1
review = Review.create!(content: 'hogehoge', user_id: user.id)

# 2
review = Review.create!(content: 'hogehoge', user: user)

# 3
review = user.reviews.create!(content: 'hogehoge')

Das Einfügen von SQL ist für 1-3 identisch, es gibt jedoch einen Unterschied zwischen dem SQL vor der Erstellung und dem Cache-Status nach der Erstellung. Lassen Sie uns mit irb überprüfen.

#1


#Anzahl der Bewertungen vor der Erstellung
irb(main):051:0> user.reviews.size
=> 19

#Benutzer wurde vor der Erstellung ausgewählt!
irb(main):054:0> review = Review.create!(content: 'hogehoge', user_id: user.id)
   (0.4ms)  BEGIN
  User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  Review Create (0.7ms)  INSERT INTO `reviews` (`content`, `user_id`, `created_at`, `updated_at`) VALUES ('hogehoge', 1, '2020-06-15 14:46:13.461715', '2020-06-15 14:46:13.461715')
   (2.0ms)  COMMIT
=> #<Review:0x000055fc16072188
 id: 20,

#Der Benutzer wird in der zurückgegebenen Bewertung zwischengespeichert
#→ Wird der Benutzer zum Zeitpunkt der Erstellung für diesen Cache gewonnen?
irb(main):055:0> review.user
=> #<User:0x000055fc16076c60

#Ursprünglicher Benutzer.Die Anzahl der Überprüfungen wurde nicht erhöht, da sie mit dem Cache vor der Erstellung identisch sind
irb(main):056:0> user.reviews.size
=> 19
#Zum Aktualisieren ist ein erneutes Laden erforderlich
irb(main):057:0> user.reviews.reload.size
  Review Load (0.9ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1
=> 20

#2


#Anzahl der Bewertungen vor der Erstellung
irb(main):058:0> user.reviews.size
=> 20

#Keine Auswahl vor der Erstellung
irb(main):059:0> review = Review.create!(content: 'hogehoge', user: user)
   (0.4ms)  BEGIN
  Review Create (0.6ms)  INSERT INTO `reviews` (`content`, `user_id`, `created_at`, `updated_at`) VALUES ('hogehoge', 1, '2020-06-15 14:53:06.510290', '2020-06-15 14:53:06.510290')
   (3.4ms)  COMMIT
=> #<Review:0x000055fc16fa6690
 id: 21,

#Der Benutzer wird in der zurückgegebenen Bewertung zwischengespeichert
#→ Es scheint, dass das zum Erstellen übergebene Benutzerobjekt zwischengespeichert ist
irb(main):060:0> review.user
=> #<User:0x000055fc16b28b40
 id: 1,

#Ursprünglicher Benutzer.Die Anzahl der Überprüfungen wurde nicht erhöht, da sie mit dem Cache vor der Erstellung identisch sind
irb(main):061:0> user.reviews.size
=> 20
#Zum Aktualisieren ist ein erneutes Laden erforderlich
irb(main):062:0> user.reviews.reload.size
  Review Load (0.8ms)  SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`user_id` = 1
=> 21

#3


#Anzahl der Bewertungen vor der Erstellung
irb(main):063:0> user.reviews.size
=> 21

#Keine Auswahl vor der Erstellung
irb(main):064:0> review = user.reviews.create!(content: 'hogehoge')
   (0.6ms)  BEGIN
  Review Create (0.6ms)  INSERT INTO `reviews` (`content`, `user_id`, `created_at`, `updated_at`) VALUES ('hogehoge', 1, '2020-06-15 14:55:45.393655', '2020-06-15 14:55:45.393655')
   (1.8ms)  COMMIT
=> #<Review:0x000055fc15fd6120
 id: 22,

#Der Benutzer wird in der zurückgegebenen Bewertung zwischengespeichert
irb(main):065:0> review.user
=> #<User:0x000055fc16b28b40
 id: 1,

# user.Auch zu Bewertungen hinzugefügt
irb(main):066:0> user.reviews.size
=> 22

In allen Mustern hat das von create zurückgegebene Überprüfungsobjekt das Benutzerobjekt zwischengespeichert. Im Fall von 1 wurde die select-Anweisung jedoch vor der Erstellung ausgegeben. Wenn Sie ein Zuordnungsobjekt haben, scheint es effizienter zu sein, das Objekt zu übergeben. Außerdem wird nur im Fall von 3 die erstellte Überprüfung zu "user.reviews" hinzugefügt.

Beim Aktualisieren verwandter Daten ist es besser, die Zuordnung zu verwenden, da diese zu den Originaldaten hinzugefügt wird und effizienter verarbeitet werden kann. Selbst wenn Sie die Zuordnung nicht verwenden, scheint es effizienter zu sein, die Zuordnung als Objekt wie in 2 zu übergeben, da die select-Anweisung nicht unnötig ausgegeben wird.

Schließlich

Ich dachte, ich wüsste etwas über den Cache, aber bis jetzt hatte ich das Verhalten, das select vor dem 1. und 2. Erstellen in [Zum Zeitpunkt der Erstellung] geschrieben hatte, nicht bemerkt ... Es scheint, dass es viele Dinge gibt, die Sie nicht bemerkt haben, wenn Sie es nicht bewusst versuchen.

Recommended Posts

[Rails] Unterdrücken Sie nutzloses SQL, indem Sie die Cache-Steuerung von Zuordnungen verwenden
Eine Überprüfung des von Rails-Anfängern verwendeten Codes
[Rails] Registrieren Sie sich mit Devise nach Attributen desselben Modells
[Rails] So zeigen Sie eine Liste der Beiträge nach Kategorie an
Ersetzen Sie die Vorschau durch Hochladen, indem Sie auf das Bild in file_field of Rails klicken
[Rails] Einführung von Rubocop durch Anfänger
[Schienen] Überprüfen Sie den Inhalt des Objekts
Erläuterung der Reihenfolge der Schienenrouten
Überprüfen Sie den Migrationsstatus von Schienen
Strict_loading-Funktion zur Unterdrückung des Auftretens von N + 1-Problemen, die von Schienen hinzugefügt wurden 6.1
So machen Sie https zum Schema der URL, die vom URL-Helfer von Rails generiert wurde