Jeden Tag erhalte ich einen Dokumentenbestand (ein Update). Ich möchte jedes Element einfügen, das noch nicht vorhanden ist.
- Ich möchte auch verfolgen, wann ich sie zum ersten Mal eingefügt habe und wann ich sie das letzte Mal in einem Update gesehen habe.
- Ich möchte keine doppelten Dokumente haben.
- Ich möchte kein Dokument entfernen, das zuvor gespeichert wurde, aber nicht in meinem Update enthalten ist.
- 95% (geschätzt) der Aufzeichnungen sind von Tag zu Tag unverändert.
Ich benutze den Python-Treiber (Pymongo).
Was ich derzeit mache, ist (Pseudocode):
for each document in update:
existing_document = collection.find_one(document)
if not existing_document:
document['insertion_date'] = now
else:
document = existing_document
document['last_update_date'] = now
my_collection.save(document)
Mein Problem ist, dass es sehr langsam ist (40 Minuten für weniger als 100 000 Datensätze, und ich habe Millionen davon im Update). Ich bin mir ziemlich sicher, dass dafür etwas eingebaut ist, aber das Dokument für update () ist mmmhhh .... ein bisschen knapp .... ( http://www.mongodb.org/display/DOCS/Updating )
Kann jemand raten, wie es schneller geht?
Ab MongoDB 2.4 können Sie $ setOnInsert ( http://docs.mongodb.org/manual/reference/operator/setOnInsert/ ) verwenden.
Setzen Sie 'insertion_date' mit $ setOnInsert und 'last_update_date' mit $ set in Ihrem Upsert-Befehl.
So verwandeln Sie Ihren Pseudocode in ein funktionierendes Beispiel:
quelle
Sie können jederzeit einen eindeutigen Index erstellen, wodurch MongoDB eine widersprüchliche Speicherung ablehnt. Betrachten Sie Folgendes mit der Mongodb-Shell:
quelle
Sie können Upsert mit dem Operator $ setOnInsert verwenden.
quelle
1. Verwenden Sie Update.
Verwenden Sie Update aus Van Nguyens Antwort oben, anstatt zu speichern. Dadurch erhalten Sie Zugriff auf die Upsert-Option.
HINWEIS : Diese Methode überschreibt das gesamte Dokument, wenn es gefunden wird ( aus den Dokumenten ).
1.a. Verwenden Sie $ set
Wenn Sie eine Auswahl des Dokuments aktualisieren möchten, aber nicht das Ganze, können Sie die $ set-Methode mit update verwenden. (wieder aus den Dokumenten ) ... Also, wenn Sie einstellen möchten ...
Senden Sie es als ...
Dies verhindert, dass versehentlich alle Dokumente mit überschrieben werden
{ name: 'jason borne' }
.quelle
Zusammenfassung
Beachten Sie, ich gehe davon aus, dass PyMongo sich an die Sprache Ihrer Wahl anpasst.
Anleitung:
Erstellen Sie die Sammlung mit einem Index mit unique = true, damit Sie keine doppelten Datensätze erhalten.
Durchlaufen Sie Ihre Eingabedatensätze und erstellen Sie Stapel von etwa 15.000 Datensätzen. Erstellen Sie für jeden Datensatz im Stapel ein Diktat, das aus den Daten besteht, die Sie einfügen möchten, wobei davon ausgegangen wird, dass jeder Datensatz ein neuer Datensatz ist. Fügen Sie diesen die "erstellten" und "aktualisierten" Zeitstempel hinzu. Geben Sie dies als Batch-Einfügebefehl mit dem Flag 'ContinueOnError' = true aus, sodass das Einfügen von allem anderen auch dann erfolgt, wenn sich dort ein doppelter Schlüssel befindet (wie es sich anhört). Dies wird sehr schnell geschehen. Bulk fügt Rock ein, ich habe 15k / Sekunde Leistungsstufen erreicht. Weitere Hinweise zu ContinueOnError finden Sie unter http://docs.mongodb.org/manual/core/write-operations/
Aufnahmeeinsätze erfolgen SEHR schnell, sodass Sie mit diesen Einsätzen in kürzester Zeit fertig sind. Jetzt ist es Zeit, die relevanten Datensätze zu aktualisieren. Führen Sie dies mit einem Stapelabruf durch, der viel schneller als einer nach dem anderen ist.
Durchlaufen Sie erneut alle Ihre Eingabedatensätze und erstellen Sie Stapel von etwa 15 KB. Extrahieren Sie die Schlüssel (am besten, wenn es einen Schlüssel gibt, aber es kann nicht geholfen werden, wenn es keinen gibt). Rufen Sie diese Datensätze mit einer Abfrage db.collectionNameBlah.find ({field: {$ in: [1, 2,3 ...})) aus Mongo ab. Stellen Sie für jeden dieser Datensätze fest, ob ein Update vorliegt, und geben Sie in diesem Fall das Update aus, einschließlich der Aktualisierung des 'aktualisierten' Zeitstempels.
Leider sollten wir beachten, dass MongoDB 2.4 und niedriger KEINE Massenaktualisierung enthalten. Sie arbeiten daran.
Wichtige Optimierungspunkte:
quelle
Ich glaube nicht, dass Mongodb diese Art des selektiven Upsertings unterstützt. Ich habe das gleiche Problem wie LeMiz und die Verwendung von update (Kriterien, newObj, upsert, multi) funktioniert nicht richtig, wenn sowohl ein "erstellter" als auch ein "aktualisierter" Zeitstempel verwendet wird. Angesichts der folgenden Upsert-Aussage:
Szenario 1 - Dokument mit 'Name' von 'abc' existiert nicht: Neues Dokument wird mit 'Name' = 'abc', 'erstellt' = 2010-07-14 11:11:11 und 'aktualisiert' = erstellt 2010-07-14 11:11:11.
Szenario 2 - Dokument mit 'Name' von 'abc' existiert bereits mit folgendem: 'Name' = 'abc', 'erstellt' = 2010-07-12 09:09:09 und 'aktualisiert' = 2010-07 -13 10:10:10. Nach dem Upsert entspricht das Dokument nun dem Ergebnis in Szenario 1. In einem Upsert kann nicht angegeben werden, welche Felder beim Einfügen festgelegt werden und welche Felder beim Aktualisieren in Ruhe gelassen werden.
Meine Lösung bestand darin, einen eindeutigen Index für die Kriterienfelder zu erstellen, eine Einfügung durchzuführen und unmittelbar danach eine Aktualisierung nur für das Feld "Aktualisiert" durchzuführen.
quelle
Im Allgemeinen ist die Verwendung von Update in MongoDB besser, da das Dokument nur erstellt wird, wenn es noch nicht vorhanden ist, obwohl ich nicht sicher bin, wie ich das mit Ihrem Python-Adapter tun soll.
Zweitens, wenn Sie nur wissen müssen, ob dieses Dokument vorhanden ist oder nicht, ist count (), das nur eine Nummer zurückgibt, eine bessere Option als find_one, das angeblich das gesamte Dokument von Ihrer MongoDB überträgt und unnötigen Datenverkehr verursacht.
quelle