2017-04-25 1 views
0

Ich frage mich, was besser Leistung/Speicher ist: Iterieren über alle Objekte in einer Sammlung und Aufruf von set/add_to_set oder Aufruf von set/add_to_set direkt auf die Kriterien oder mit aktualisiere alle mit set/add_to_set.

# update_all 
User.where(some_query).update_all(
    { 
    '$addToSet': { 
     :'some.field.value' => :value_to_add 
    } 
    } 
) 

# each do + add_to_set 
User.where(some_query).each do |user| 
    user.add_to_set(:'some.field.value' => :value_to_add) 
end 

# Criteria#add_to_set 
User.where(some_query).add_to_set(
    :'some.field.value' => :value_to_add 
) 

Jede Eingabe wird geschätzt. Vielen Dank!

Antwort

1

Ich startete MongoDB Server mit ausführlichen Flagge. Das habe ich bekommen.

Option 1. update_all auf einem Selektor angewendet

2017-04-25 COMMAND command production_v3.$cmd command: update { update: "products", updates: [ { q: { ... }, u: { $addToSet: { test_field: "value_to_add" } }, multi: true, upsert: false } ], ordered: true } 

I eine Ausgabe entfernt, so dass leichter zu lesen ist. Der Ablauf ist:

  • MongoID generiert einen einzelnen Befehl mit Abfrage und Update angegeben.
  • MongoDB-Server erhält den Befehl. Es geht durch Sammlung und aktualisiert jedes Match in [vage] one go.

Hinweis! Sie können aus dem Quellcode lernen oder als gegeben annehmen. Da MongoID gemäß meiner Terminologie den Befehl zum Senden in Schritt 1 generiert, werden Ihre Modelle nicht überprüft. z.B. Wenn 'some.field.value' nicht zu Ihrem Feld im Modellbenutzer gehört, wird der Befehl weiterhin ausgeführt und verbleibt in der Datenbank.

Option 2. jeweils an einem Wähler

ich Befehle erhalten finden wie unten gefolgt von mehreren GETMORE-s:

2017-04-25 COMMAND command production_v3.products command: find { find: "products", filter: { ... } } 0ms 

ich auch eine große Anzahl von update-s erhalten:

2017-04-25 COMMAND command production_v3.$cmd command: update { update: "products", updates: [ { q: { _id: ObjectId('52a6db196c3f4f422500f255') }, u: { $addToSet: { test_field: { $each: [ "value_to_add" ] } } }, multi: false, upsert: false } ], ordered: true } 0ms 

Der Durchfluss unterscheidet sich radikal von der ersten Option:

  • MongoID sendet eine einfache Abfrage an den MongoDB-Server. Wenn Ihre Auflistung groß genug ist und die Abfrage einen wesentlichen Teil davon abdeckt, passiert Folgendes in einer Schleife:
  • [Schleife] Reagieren Sie mit einer Teilmenge aller Übereinstimmungen. Lassen Sie den Rest für die nächste Iteration.
  • [loop] MongoID erhält ein Array von passenden Elementen im Hash-Format. MongoID analysiert jeden Eintrag und initialisiert die Benutzerklasse dafür. Das ist eine teure Operation!
  • [loop] MongoID generiert für jede Benutzerinstanz aus dem vorherigen Schritt ein Aktualisierungskommando und sendet es an serve. Steckdosen sind auch teuer.
  • [Schleife] MongoDB erhält den Befehl und durchläuft die Sammlung bis zum ersten Treffer. Aktualisiert das Spiel. Es ist schnell, addiert sich aber einmal in einer Schleife.
  • [Schleife] MongoID analysiert die Antwort und aktualisiert ihre Benutzerinstanz entsprechend. Teuer und unnötig.

Option 3. add_to_set auf einem Selektor angewandt

Unter der Haube ist es gleichbedeutend mit Option 1. Die CPU und Speicher-Overhead für das Wohl der Frage unerheblich ist.

Fazit.

Option 2 ist so viel langsamer, dass Benchmarking keinen Sinn macht. In dem speziellen Fall, den ich versuche, führte dies zu einer 1000-prozentigen Anfrage an MongoDB und einer 1000er User Class-Initialisierung. Die Optionen 1 und 3 führten zu einer einzigen Anfrage an MongoDB und beruhten auf der hochoptimierten Engine von MongoDB.