web-dev-qa-db-fra.com

MongoDB - quel est le moyen le plus rapide de mettre à jour tous les enregistrements d'une collection?

J'ai une collection avec 9 millions de disques. J'utilise actuellement le script suivant pour mettre à jour la collection entière:

simple_update.js

db.mydata.find().forEach(function(data) {
  db.mydata.update({_id:data._id},{$set:{pid:(2571 - data.Y + (data.X * 2572))}});
});

Ceci est exécuté à partir de la ligne de commande comme suit:

mongo my_test simple_update.js

Donc tout ce que je fais c'est ajouter un nouveau champ pid basé sur un calcul simple.

Y at-il un moyen plus rapide? Cela prend beaucoup de temps.

38
mattjvincent

Vous pouvez faire deux choses.

  1. Envoyez une mise à jour avec l'indicateur "multi" défini sur true.
  2. Stockez la fonction côté serveur et essayez d'utiliser exécution de code côté serveur .

Ce lien contient également les conseils suivants:

Il s'agit d'une bonne technique pour effectuer des travaux administratifs par lots. Exécutez mongo sur le serveur, en vous connectant via l'interface localhost. La connexion est alors très rapide et à faible latence. C'est plus convivial que db.eval () car db.eval () bloque les autres opérations.

C'est probablement le plus rapide que vous obtiendrez. Vous devez réaliser que l'émission de mises à jour 9M sur un seul serveur va être une opération lourde. Disons que vous pouvez obtenir 3 000 mises à jour/seconde, vous parlez toujours de fonctionner pendant près d'une heure.

Et ce n'est pas vraiment un "problème de mongo", ça va être une limitation matérielle.

28
Gates VP

J'utilise la: méthode db.collection.update

// db.collection.update( criteria, objNew, upsert, multi ) // --> for reference
db.collection.update( { "_id" : { $exists : true } }, objNew, upsert, true);
18
Telmo Dias

Je ne recommanderai pas d'utiliser {multi: true} pour un ensemble de données plus volumineux, car il est moins configurable.

Une meilleure façon d'utiliser l'insert en vrac.

L'opération en bloc est vraiment utile pour les tâches du planificateur. Supposons que vous deviez supprimer les données de plus de 6 mois par jour. Utilisez l'opération en vrac. Son serveur rapide et ne ralentira pas. Le processeur, l'utilisation de la mémoire ne sont pas visibles lorsque vous insérez, supprimez ou mettez à jour plus d'un milliard de documents. J'ai trouvé {multi: true} ralentir le serveur lorsque vous traitez avec plus d'un million de documents (nécessite plus de recherche à ce sujet.)

Voir un exemple ci-dessous. C'est un script js Shell, peut également l'exécuter sur le serveur en tant que programme de noeud (utilisez le module npm shelljs ou similaire pour y parvenir)

mettre à jour mongo vers 3.2+

La façon normale de mettre à jour plusieurs documents uniques est

let counter = 0;
db.myCol.find({}).sort({$natural:1}).limit(1000000).forEach(function(document){
    counter++;
    document.test_value = "just testing" + counter
    db.myCol.save(document)
});

Cela a pris 310-315 secondes quand j'ai essayé. C'est plus de 5 minutes pour mettre à jour un million de documents.

Ma collection comprend plus de 100 millions de documents, donc la vitesse peut différer pour les autres.

La même chose en utilisant l'insert en vrac est

    let counter = 0;
// magic no.- depends on your hardware and document size. - my document size is around 1.5kb-2kb
// performance reduces when this limit is not in 1500-2500 range.
// try different range and find fastest bulk limit for your document size or take an average.
let limitNo = 2222; 
let bulk = db.myCol.initializeUnorderedBulkOp();
let noOfDocsToProcess = 1000000;
db.myCol.find({}).sort({$natural:1}).limit(noOfDocsToProcess).forEach(function(document){
    counter++;
    noOfDocsToProcess --;
    limitNo--;
    bulk.find({_id:document._id}).update({$set:{test_value : "just testing .. " + counter}});
    if(limitNo === 0 || noOfDocsToProcess === 0){
        bulk.execute();
        bulk = db.myCol.initializeUnorderedBulkOp();
        limitNo = 2222;
    }
});

Le meilleur temps était de 8972 millis. Ainsi, en moyenne, il n'a fallu que 10 secondes pour mettre à jour un million de documents. 30 fois plus rapide qu'à l'ancienne.

Mettez le code dans un fichier .js et exécutez-le en tant que script Shell Mongo.

Si quelqu'un a trouvé un meilleur moyen, veuillez le mettre à jour. Permet d'utiliser mongo de manière plus rapide.

3
shijin

A partir de Mongo 4.2, db.collection.update() peut accepter un pipeline d'agrégation, permettant enfin la mise à jour/création d'un champ basé sur un autre champ; et nous permettant ainsi d'appliquer pleinement ce type de requête côté serveur:

// { Y: 456,  X: 3 }
// { Y: 3452, X: 2 }
db.collection.update(
  {},
  [{ $set: { pid: {
    $sum: [ 2571, { $multiply: [ -1, "$Y" ] }, { $multiply: [ 2572, "$X" ] } ]
  }}}],
  { multi: true }
)
// { Y: 456,  X: 3, pid: 9831 }
// { Y: 3452, X: 2, pid: 4263 }
  • La première partie {} Est la requête de correspondance, filtrant les documents à mettre à jour (tous les documents dans ce cas).

  • La deuxième partie [{ $set: { pid: ... } }] Est le pipeline d'agrégation de mise à jour (notez les crochets indiquant l'utilisation d'un pipeline d'agrégation). $set est un nouvel opérateur d'agrégation et un alias de $addFields. Notez comment pid est créé directement sur la base des valeurs de X ($X) Et Y ($Y) À partir du même document.

  • N'oubliez pas { multi: true }, Sinon seul le premier document correspondant sera mis à jour.

0
Xavier Guihot

Je ne sais pas si ce sera plus rapide, mais vous pouvez faire une mise à jour multiple. Dis le update where _id > 0 (cela sera vrai pour chaque objet), puis définissez le drapeau 'multi' sur true et il devrait faire de même sans avoir à parcourir toute la collection.

Vérifiez ceci: MongoDB - Exécution de code côté serveur

0
Gandalf