39 votes

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 bloque et est lente).

sur http://www.mongodb.org/display/DOCS/Excessive+Disque+Espace

Je me demande comment faire pour que le mongodb libère l'espace disque supprimé. automatiquement ?

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.

65voto

Justin Jenkins Points 10501

En général, si vous n'avez pas besoin de réduire vos fichiers de données, vous ne devriez pas les réduire du tout. En effet, la "croissance" de vos fichiers de données sur le disque est une opération assez coûteuse et plus MongoDB peut allouer d'espace aux 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 fait croître ses fichiers de données de la manière suivante doublement, donc les fichiers de données peuvent être 64 Mo, puis 128 Mo, etc. jusqu'à 2 Go (où il arrête de doubler pour point où il arrête de doubler pour conserver les fichiers jusqu'à 2 Go).

  2. Comme avec la plupart des bases de données ... pour faire des opérations comme le rétrécissement, vous vous devez planifier un travail séparé pour le faire, il n'y a pas de "autoshrink" dans MongoDB. En fait, parmi les principales bases de données noSQL (je déteste ce nom), seul Riak permet l'autoshrink. Vous devrez donc créer une tâche en utilisant le planificateur de votre système d'exploitation pour exécuter un rétrécissement. Vous pourriez utiliser un bash script, ou faire exécuter par un job un php script, etc.

Javascript côté serveur

Vous pouvez utiliser le Javascript côté serveur pour effectuer le rétrécissement et exécuter ce JS via le shell de Mongo sur une base régulière par le biais d'un travail (comme cron ou le service de planification de Windows) ....

En supposant qu'une collection appelée foo vous devez sauvegarder le javascript ci-dessous dans un fichier appelé bar.js et courir ...

$ 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 va s'exécuter et retourner 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 cette opération selon un calendrier (en dehors des heures de pointe) et vous êtes prêt à partir.

Collections plafonnées

Il existe toutefois une autre option, collections plafonnées .

Les collections plafonnées sont de taille fixe fixes qui ont une fonction de vieillissement performance de vieillissement automatique de l'OFI (le vieillissement est basé sur l'ordre d'insertion). Elles ressemblent un peu au concept de "RRD". si vous le connaissez.

En outre, les collectes plafonnées automatiquement, avec des performances élevées, maintiennent l'ordre d'insertion des objets de la collection, ce qui est très puissant pour certains cas d'utilisation comme la journalisation.

En gros, vous pouvez limiter la taille (ou le nombre de documents) d'une collection à, disons, 20 Go. 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 qu'elles arrivent.

C'est un excellent moyen de conserver une grande quantité de données, en éliminant les anciennes au fur et à mesure que le temps passe et en conservant la même quantité d'espace disque utilisé.

26voto

Mojo Points 1136

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

Vous devez utiliser un jeu de répliques.

Je pense qu'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-la et laissez-la se resynchroniser avec le maître.

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

De plus, cela ne peut pas être automatisé. Eh bien, ça pourrait, mais je ne pense pas que je suis prêt à essayer.

8voto

Robert Jobson Points 61

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

Si vous avez peu de collections à conserver ou si vous ne voulez qu'un sous-ensemble des données, vous pouvez déplacer les données à 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 alors les déplacer à nouveau dans une nouvelle base de données portant le même nom. Veillez simplement à recréer tous 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/dépôt/importation pour les bases de données comportant de nombreuses collections permettrait probablement d'obtenir le même résultat, mais je ne l'ai pas testée.

En outre, vous pouvez conserver les collections permanentes dans une base de données distincte de vos données transitoires/de traitement et abandonner simplement la base de données de traitement une fois vos travaux terminés. Comme MongoDB est sans schéma, rien, à l'exception des index, ne sera perdu et votre base de données et vos collections seront recréées lors de l'exécution des insertions pour les processus suivants. Assurez-vous simplement que vos tâches incluent la création de tous les index nécessaires au moment opportun.

4voto

Adam Comerford Points 9084

Si vous utilisez jeux de répliques qui n'étaient pas disponibles au moment de la rédaction de cette question, vous pouvez mettre en place un processus de récupération automatique de l'espace sans subir de perturbations importantes ou de problèmes de performance.

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

En exécutant cette opération sur les secondaires (puis en réduisant le primaire et en répétant le processus), vous pouvez effectivement récupérer de l'espace disque sur l'ensemble du jeu avec un minimum de perturbations. Vous devez être prudent si vous lisez à partir des secondaires, car cela mettra un secondaire hors rotation pour une période potentiellement longue. Vous devez également vous assurer que votre oplog est suffisante pour réussir une resynchronisation, mais c'est généralement quelque chose dont vous voulez être sûr, que vous le fassiez ou non.

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

NOTE : IL S'AGIT ESSENTIELLEMENT D'UN PSEUDO-CODE - À DES FINS D'ILLUSTRATION UNIQUEMENT - NE PAS UTILISER POUR DES SYSTÈMES DE PRODUCTION SANS MODIFICATIONS IMPORTANTES.

#!/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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X