web-dev-qa-db-fra.com

Compacter automatiquement l'espace supprimé dans mongodb?

Le document mongodb dit que

Pour compacter cet espace, exécutez db.repairDatabase () à partir du shell mongo (notez que cette opération va bloquer et est lente).

dans http://www.mongodb.org/display/DOCS/Excessive+Disk+Space

Je me demande comment créer automatiquement l'espace disque supprimé de mongodb ?

p.s. Nous avons stocké de nombreuses tâches de téléchargement dans mongodb, jusqu'à 20 Go, et les avons terminées en une demi-heure.

39
Zealot Ke

En général, si vous n'avez pas besoin de réduire vos fichiers de données, vous ne devriez pas du tout les réduire. En effet, "augmenter" vos fichiers de données sur le disque est une opération assez coûteuse et plus MongoDB peut allouer d'espace dans les fichiers de données, moins vous aurez de fragmentation.

Vous devez donc essayer de fournir autant d'espace disque que possible pour la base de données.

Cependant si vous devez réduire la base de données, vous devez garder deux choses à l'esprit.

  1. MongoDB augmente ses fichiers de données en doublant, de sorte que les fichiers de données peuvent atteindre 64 Mo, puis 128 Mo, etc. jusqu'à 2 Go (à ce moment, il cesse de doubler pour conserver les fichiers jusqu'à 2 Go.)

  2. Comme avec la plupart des bases de données ... pour effectuer des opérations telles que la réduction, vous devez planifier un travail distinct pour le faire, il n'y a pas de "rétrécissement automatique" dans MongoDB. En fait, des principales bases de données noSQL (détestent ce nom), seul Riak rétrécira automatiquement. Vous devrez donc créer un travail à l'aide du planificateur de votre système d'exploitation pour exécuter une réduction. Vous pouvez utiliser un script bash, ou demander à un travail d'exécuter un script php, etc.

Javascript côté serveur

Vous pouvez utiliser Javascript côté serveur pour effectuer la réduction et exécuter ce JS via le shell de Mongo sur une base régulière via un travail (comme Cron ou le service de planification Windows) ...

En supposant une collection appelée foo , vous enregistrez le javascript ci-dessous dans un fichier appelé bar.js et exécutez ...

$ mongo foo bar.js

Le fichier javascript ressemblerait à quelque chose comme ...

// Get a the current collection size.
var storage = db.foo.storageSize();
var total = db.foo.totalSize();

print('Storage Size: ' + tojson(storage));

print('TotalSize: ' + tojson(total));

print('-----------------------');
print('Running db.repairDatabase()');
print('-----------------------');

// Run repair
db.repairDatabase()

// Get new collection sizes.
var storage_a = db.foo.storageSize();
var total_a = db.foo.totalSize();

print('Storage Size: ' + tojson(storage_a));
print('TotalSize: ' + tojson(total_a));

Cela fonctionnera et retournera quelque chose comme ...

MongoDB Shell version: 1.6.4
connecting to: foo
Storage Size: 51351
TotalSize: 79152
-----------------------
Running db.repairDatabase()
-----------------------
Storage Size: 40960
TotalSize: 65153

Exécutez cela selon un horaire (pendant les heures creuses) et vous êtes prêt à partir.

Collections plafonnées

Cependant, il existe une autre option, collections plafonnées .

Les collections plafonnées sont des collections de taille fixe qui ont une fonction d'expiration automatique FIFO très haute performance (l'expiration est basée sur l'ordre d'insertion). Ils sont un peu comme le concept "RRD" si vous le connaissez.

De plus, les collections plafonnées automatiquement, avec des performances élevées, maintiennent l'ordre d'insertion des objets dans la collection; c'est très puissant pour certains cas d'utilisation tels que la journalisation.

Fondamentalement, vous pouvez limiter la taille (ou le nombre de documents dans) une collection à dire .. 20 Go et une fois cette limite atteinte, MongoDB commencera à jeter les enregistrements les plus anciens et à les remplacer par des entrées plus récentes au fur et à mesure de leur entrée.

C'est un excellent moyen de conserver une grande quantité de données, de supprimer les anciennes données au fil du temps et de conserver la même quantité d'espace disque utilisé.

65
Justin Jenkins

J'ai une autre solution qui pourrait mieux fonctionner que de faire db.repairDatabase () si vous ne pouvez pas vous permettre de verrouiller le système ou si vous n'avez pas le double de stockage.

Vous devez utiliser un jeu de réplicas.

Ma pensée est une fois que vous avez supprimé toutes les données excédentaires qui engloutissent votre disque, arrêtez une réplique secondaire, effacez son répertoire de données, démarrez-le et laissez-le se resynchroniser avec le maître.

Le processus prend du temps, mais il ne devrait vous coûter que quelques secondes de temps d'arrêt, lorsque vous effectuez le rs.stepDown ().

Cela ne peut pas non plus être automatisé. C'est possible, mais je ne pense pas que je suis prêt à essayer.

26
Mojo

L'exécution de db.repairDatabase () nécessite que vous disposiez d'un espace égal à la taille actuelle de la base de données disponible sur le système de fichiers. Cela peut être gênant lorsque vous savez que les collections restantes ou les données que vous devez conserver dans la base de données utiliseraient actuellement beaucoup moins d'espace que ce qui est alloué et que vous n'avez pas assez d'espace pour effectuer la réparation.

Comme alternative, si vous avez peu de collections dont vous avez réellement besoin pour conserver ou ne voulez qu'un sous-ensemble des données, vous pouvez déplacer les données dont vous avez besoin pour conserver dans une nouvelle base de données et supprimer l'ancienne. Si vous avez besoin du même nom de base de données, vous pouvez les replacer dans une nouvelle base de données du même nom. Assurez-vous simplement de recréer les index.

use cleanup_database
db.dropDatabase();

use oversize_database

db.collection.find({},{}).forEach(function(doc){
    db = db.getSiblingDB("cleanup_database");
    db.collection_subset.insert(doc);
});

use oversize_database
db.dropDatabase();

use cleanup_database

db.collection_subset.find({},{}).forEach(function(doc){
    db = db.getSiblingDB("oversize_database");
    db.collection.insert(doc);
});

use oversize_database

<add indexes>
db.collection.ensureIndex({field:1});

use cleanup_database
db.dropDatabase();

Une opération d'exportation/suppression/importation de bases de données avec de nombreuses collections permettrait probablement d'obtenir le même résultat, mais je n'ai pas testé.

De plus, vous pouvez conserver des collections permanentes dans une base de données distincte de vos données transitoires/de traitement et simplement supprimer la base de données de traitement une fois vos travaux terminés. Étant donné que MongoDB est sans schéma, rien sauf les index ne serait perdu et votre base de données et vos collections seront recréées lors de la prochaine insertion des processus. Assurez-vous simplement que vos travaux incluent la création d'index nessecary à un moment approprié.

8
Robert Jobson

Si vous utilisez jeux de réplicas , qui n'étaient pas disponibles lorsque cette question a été écrite à l'origine, vous pouvez configurer un processus pour récupérer automatiquement de l'espace sans encourir de perturbations importantes ni de problèmes de performances.

Pour ce faire, vous profitez des capacités de synchronisation initiale automatique d'un secondaire dans un jeu de réplicas. Pour expliquer: si vous arrêtez un secondaire, effacez ses fichiers de données et redémarrez-le, le secondaire se resynchronisera à partir de zéro de l'un des autres nœuds de l'ensemble (par défaut, il choisit le nœud le plus proche en regardant la réponse ping fois). Lorsque cette resynchronisation se produit, toutes les données sont réécrites à partir de zéro (y compris les index), font en fait la même chose qu'une réparation et l'espace disque récupéré.

En exécutant cela sur des secondaires (puis en descendant le primaire et en répétant le processus), vous pouvez récupérer efficacement l'espace disque sur l'ensemble avec un minimum de perturbations. Vous devez être prudent si vous lisez à partir de secondaires, car cela prendra un secondaire hors rotation pendant un temps potentiellement long. Vous voulez également vous assurer que votre fenêtre oplog est suffisante pour effectuer une resynchronisation réussie, mais c'est généralement quelque chose dont vous voudrez vous assurer si vous le faites ou non.

Pour automatiser ce processus, il vous suffirait d'avoir un script exécuté pour effectuer cette action sur des jours différents (ou similaires) pour chaque membre de votre ensemble, de préférence pendant votre temps de silence ou votre fenêtre de maintenance. Une version très naïve de ce script ressemblerait à ceci dans bash:

REMARQUE: CECI IS CODE PSEUDO DE BASE - À DES FINS ILLUSTRATIVES UNIQUEMENT - NE PAS UTILISER POUR DES SYSTÈMES DE PRODUCTION SANS CHANGEMENTS SIGNIFICATIFS

#!/bin/bash 

# First arg is Host MongoDB is running on, second arg is the MongoDB port

MONGO=/path/to/mongo
MONGOHOST=$1
MONGOPORT=$2
DBPATH = /path/to/dbpath

# make sure the node we are connecting to is not the primary
while (`$MONGO --quiet --Host $MONGOHOST --port $MONGOPORT --eval 'db.isMaster().ismaster'`)
do
    `$MONGO --quiet --Host $MONGOHOST --port $MONGOPORT --eval 'rs.stepDown()'`
    sleep 2
done    
echo "Node is no longer primary!\n"

# Now shut down that server 
# something like (assuming user is set up for key based auth and has password-less Sudo access a la ec2-user in EC2)
ssh -t user@$MONGOHOST Sudo service mongodb stop

# Wipe the data files for that server

ssh -t user@$MONGOHOST Sudo rm -rf $DBPATH
ssh -t user@$MONGOHOST Sudo mkdir $DBPATH
ssh -t user@$MONGOHOST Sudo chown mongodb:mongodb $DBPATH

# Start up server again
# similar to shutdown something like 
ssh -t user@$MONGOHOST Sudo service mongodb start 
4
Adam Comerford